Tags
Template tags are wrapped in {% %} delimiters. They control layout inheritance, partial inclusion, iteration, and conditionals.
extends
Declares that this template inherits from a layout. Must be the first token in the file.
{% extends "layout.tpl" %}
The layout file path is relative to the active theme's root views directory. The child template must define all content inside {% block %} tags; any content outside blocks is ignored.
block / endblock
Defines a named content region. In a layout file, blocks provide default content (or are empty). In a child template, blocks override the layout's regions.
Layout (layout.tpl):
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{{ site_name }}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
{% block sidebar %}{% endblock %}
</body>
</html>
Child template:
{% extends "layout.tpl" %}
{% block title %}{{ page_title }} — {{ site_name }}{% endblock %}
{% block content %}
<article>
<h1>{{ post.title }}</h1>
{! render_content post !}
</article>
{% endblock %}
include
Injects another template file inline. The included file shares the current template's variable scope.
{% include "partials/author-card.tpl" %}
{% include "partials/pagination.tpl" %}
Path is relative to the active theme's views directory. Widget templates cannot include files from their own directory — includes always resolve against the theme.
for / endfor / empty
Iterates over an array or object collection.
{% for post in posts %}
<h2>{{ post.title }}</h2>
{% endfor %}
With empty clause (renders when the collection is empty or null):
{% for post in posts %}
<h2>{{ post.title }}</h2>
{% empty %}
<p>No posts found.</p>
{% endfor %}
Nested loops:
{% for category in categories %}
<h3>{{ category.name }}</h3>
{% for post in category.posts %}
<p>{{ post.title }}</p>
{% endfor %}
{% endfor %}
The loop variable (post, category, etc.) is scoped to the loop body. There is no automatic loop.index variable.
if / elif / else / endif
Conditional rendering. Condition is a boolean expression.
{% if post.status == "published" %}
<span class="badge badge-success">Published</span>
{% elif post.status == "scheduled" %}
<span class="badge badge-warning">Scheduled</span>
{% else %}
<span class="badge badge-secondary">Draft</span>
{% endif %}
Supported comparison operators: ==, !=, <, >, <=, >=
Boolean operators: and, or
Negation: not
{% if user and user.is_active %}
Welcome back, {{ user.username }}!
{% endif %}
{% if not post.is_premium %}
{! render_content post !}
{% endif %}
Truthiness: empty string, "0", null, empty array, and false are all falsy.