๐งฑ Tera Templates
Tera is a template engine for Rust, inspired by Jinja2 and Django templates. Dr. Jekyll includes a custom Rouge lexer that highlights Teraโs expression blocks ({{ }}), statement tags ({% %}), and comment blocks ({# #}), while treating surrounding content as plain text.
Tera syntax is also highlighted automatically inside triple-quoted (
""") values in Cliff TOML files.
Table of Contents
How to Use
Use the tera language tag on a fenced code block:
```tera
{{ page.title }}
```Expression Blocks
Expression blocks evaluate a variable or expression and output the result. Use as delimiters; the whitespace-stripping variants trim surrounding whitespace:
```tera
{{ user.name }}
{{ version | trim_start_matches(pat="v") }}
{{- timestamp | date(format="%Y-%m-%d") -}}
{{ "<REMOTE_URL>/" ~ remote.github.owner ~ "/" ~ remote.github.repo -}}
```
{{ user.name }}
{{ version | trim_start_matches(pat="v") }}
{{- timestamp | date(format="%Y-%m-%d") -}}
{{ "<REMOTE_URL>/" ~ remote.github.owner ~ "/" ~ remote.github.repo -}}
The ~ operator concatenates strings.
Statement Tags
Statement tags control template logic. Use {% and %} as delimiters:
Conditionals
```tera
{% if user.is_admin %}
<p>Welcome, admin.</p>
{% elif user.is_logged_in %}
<p>Welcome, {{ user.name }}.</p>
{% else %}
<p>Please log in.</p>
{% endif %}
```
{% if user.is_admin %}
<p>Welcome, admin.</p>
{% elif user.is_logged_in %}
<p>Welcome, {{ user.name }}.</p>
{% else %}
<p>Please log in.</p>
{% endif %}
Loops
```tera
{% for commit in commits %}
- {{ commit.message | upper_first }}
{% endfor %}
```
{% for commit in commits %}
- {{ commit.message | upper_first }}
{% endfor %}
Variable Assignment
```tera
{% set greeting = "Hello, " ~ user.name ~ "!" %}
{{ greeting }}
{% set_global counter = 0 %}
```
{% set greeting = "Hello, " ~ user.name ~ "!" %}
{{ greeting }}
{% set_global counter = 0 %}
Template Inheritance
```tera
{% extends "base.html" %}
{% block content %}
<h1>{{ page.title }}</h1>
{{ super() }}
{% endblock content %}
```
{% extends "base.html" %}
{% block content %}
<h1>{{ page.title }}</h1>
{{ super() }}
{% endblock content %}
Comment Blocks
Comment blocks are not rendered in the output. Use {# and #} as delimiters:
```tera
{# This is a comment and will not appear in the rendered output #}
{#- Whitespace-stripping comment -#}
```
{# This is a comment and will not appear in the rendered output #}
{#- Whitespace-stripping comment -#}
Filters
Filters transform a value using the pipe | operator. Chaining is supported:
```tera
{{ commits | group_by(attribute="group") }}
{{ message | striptags | trim | upper_first }}
{{ body | truncate(length=200) }}
{{ items | sort | first }}
{{ count | round(method="ceil", precision=2) }}
{{ r.contributors | filter(attribute="is_first_time", value=true) }}
{{ input | trim_start_matches(pat='"') | trim_end_matches(pat='"') }}
```
{{ commits | group_by(attribute="group") }}
{{ message | striptags | trim | upper_first }}
{{ body | truncate(length=200) }}
{{ items | sort | first }}
{{ count | round(method="ceil", precision=2) }}
{{ r.contributors | filter(attribute="is_first_time", value=true) }}
{{ input | trim_start_matches(pat='"') | trim_end_matches(pat='"') }}
filteris also a block-level statement tag ({% filter lower %}...{% endfilter %}). When used after a pipe|it acts as a collection filter; the lexer highlights it as a control keyword in both cases.
Macros
Macros are reusable template fragments. self::macro_name() calls a macro defined in the same template:
```tera
{%- macro user_url(name) -%}
[@{{ name | lower }}](https://github.com/{{ name | lower }})
{%- endmacro -%}
{%- macro plural(count, singular, plural) -%}
{%- if count == 1 -%}{{ singular }}{%- else -%}{{ plural }}{%- endif -%}
{%- endmacro -%}
{{ self::user_url(name=commit.remote.username) }}
{{ self::plural(count=s_commit_count, singular="commit", plural="commits") }}
```
{%- macro user_url(name) -%}
[@{{ name | lower }}](https://github.com/{{ name | lower }})
{%- endmacro -%}
{%- macro plural(count, singular, plural) -%}
{%- if count == 1 -%}{{ singular }}{%- else -%}{{ plural }}{%- endif -%}
{%- endmacro -%}
{{ self::user_url(name=commit.remote.username) }}
{{ self::plural(count=s_commit_count, singular="commit", plural="commits") }}
Built-in Functions and Tests
```tera
{% set nums = range(end=5) %}
{% set ts = now(timestamp=true) %}
{% if value is defined %}{{ value }}{% endif %}
{% if count is odd %}odd{% endif %}
{% if name is starting_with("v") %}versioned{% endif %}
```
{% set nums = range(end=5) %}
{% set ts = now(timestamp=true) %}
{% if value is defined %}{{ value }}{% endif %}
{% if count is odd %}odd{% endif %}
{% if name is starting_with("v") %}versioned{% endif %}
Full Example
```tera
{# Changelog body template for git-cliff #}
{% if version %}
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}
## [unreleased]
{% endif %}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
{% if commit.breaking %}[**breaking**] {% endif %}\
{{ commit.message | upper_first }} โ \
[{{ commit.id | truncate(length=7, end="") }}]({{ commit.remote.link }})
{% endfor %}
{% endfor %}
```
{# Changelog body template for git-cliff #}
{% if version %}
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}
## [unreleased]
{% endif %}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
{% if commit.breaking %}[**breaking**] {% endif %}\
{{ commit.message | upper_first }} โ \
[{{ commit.id | truncate(length=7, end="") }}]({{ commit.remote.link }})
{% endfor %}
{% endfor %}