Bengal provides powerful template filters for querying, filtering, and transforming content collections. Many filters are inspired by Hugo for easy migration.
Implementation Maturity: Bengal currently ships with over 80+ native template functions and filters. While our documentation is being expanded to provide exhaustive examples for every edge case, the core implementation for Math, Strings, Collections, SEO, and Autodoc is fully production-ready and registered in the engine.
Functions vs Filters: Understanding the Difference
Bengal provides two types of template capabilities: functions and filters. Understanding when to use each helps you write clearer, more efficient templates.
Filters: Transform Values
Filters transform a value using the pipe operator (| or |>). The value on the left becomes the first argument to the filter.
Syntax:
{{ value | filter }}
{{ value | filter(arg1, arg2) }}
{{ value |> filter1 |> filter2 }}
When to use: When you have a value to transform.
Examples:
{# Transform text #}
{{ page.title | upper }}
{{ page.content | markdown | safe }}
{# Transform collections #}
{{ site.pages | where('draft', false) | sort_by('date') }}
{# Chain transformations #}
{{ text |> slugify |> truncate(50) }}
Registered in: env.filters(available via pipe operator)
Functions: Standalone Operations
Functions are called directly without a value to transform. They perform operations or retrieve data.
Syntax:
{{ function() }}
{{ function(arg1, arg2) }}
When to use: When performing an operation that doesn't transform an existing value.
Examples:
{# Retrieve data #}
{{ get_page('docs/about') }}
{{ get_data('data/authors.json') }}
{# Generate references #}
{{ ref('docs/getting-started') }}
{{ get_section('blog') }}
{# Check conditions #}
{{ page_exists('path/to/page') }}
Registered in: env.globals(available as direct function calls)
Quick Decision Guide
| Use Case | Type | Example |
|---|---|---|
| Transform text | Filter | {{ text \| upper }} |
| Transform a collection | Filter | {{ pages \| where('draft', false) }} |
| Chain transformations | Filter | {{ data \|> filter1 \|> filter2 }} |
| Retrieve a page | Function | {{ get_page('path') }} |
| Load data file | Function | {{ get_data('file.json') }} |
| Generate cross-reference | Function | {{ ref('docs/page') }} |
| Check if page exists | Function | {{ page_exists('path') }} |
Why This Matters
Filters are designed for data transformation pipelines:
- Read left-to-right:
data |> transform |> format |> output - Chain multiple operations:
pages |> filter |> sort |> limit - Functional programming style
Functions are designed for operations and lookups:
- No implicit left operand
- Direct calls:
get_page('path')not| get_page('path') - Procedural style
Common Patterns
Pattern 1: Filter a collection, then use a function
{% let blog_posts = site.pages |> where('type', 'blog') |> sort_by('date', reverse=true) %}
{% for post in blog_posts %}
{# Use function to get related page #}
{% let related = get_page(post.metadata.related) %}
{% if related %}
<a href="{{ related.url }}">Related: {{ related.title }}</a>
{% end %}
{% end %}
Pattern 2: Function to get data, filter to transform
{% let authors = get_data('data/authors.json') %}
{% let active_authors = authors |> where('active', true) |> sort_by('name') %}
Pattern 3: Function result used in filter chain
{% let section = get_section('blog') %}
{% let recent = section.pages |> sort_by('date', reverse=true) |> limit(5) %}
Note
Can't remember which is which? Ask: "Do I have a value to transform?"
- Yes → Use a filter (
|or|>) - No → Use a function (direct call)