Templates
Zorto uses the Tera template engine, which has Jinja2-like syntax.
Template hierarchy #
Zorto looks for these templates (themes provide defaults for all of them):
| Template | Used for |
|---|---|
base.html | Base layout all others extend |
index.html | Site homepage (content/_index.md) |
section.html | Section pages (content/*/_index.md) |
page.html | Individual pages |
404.html | Not-found page |
{taxonomy}/list.html | Taxonomy index (e.g., tags/list.html for /tags/) |
{taxonomy}/single.html | Single taxonomy term (e.g., tags/single.html for /tags/rust/) |
Template context #
Each template receives context variables:
| Variable | Available in | Description |
|---|---|---|
page | page.html | Current page object (title, content, date, permalink, extra, etc.) |
section | section.html, index.html | Current section object (title, pages, subsections, etc.) |
config | All | Site configuration (config.title, config.extra, etc.) |
paginator | Paginated sections | Pagination info (pages, current_index, number_pagers) |
Use page.permalink or section.permalink to get the current page’s full URL.
Custom functions #
| Function | Description |
|---|---|
get_url(path) | Get the permalink for a path |
get_section(path) | Load a section and its pages |
get_taxonomy_url(kind, name) | URL for a taxonomy term |
now() | Current timestamp |
Example:
{% set posts = get_section(path="posts/_index.md") %}
{% for page in posts.pages %}
<a href="{{ page.permalink }}">{{ page.title }}</a>
{% endfor %}
Worked example #
Here’s a complete page.html showing how template variables, filters, and blocks work together:
{% extends "base.html" %}
{% block content %}
<article>
<h1>{{ page.title }}</h1>
{% if page.date %}
<time>{{ page.date | date(format="%B %d, %Y") }}</time>
{% endif %}
{% if page.taxonomies.tags %}
<div class="tags">
{% for tag in page.taxonomies.tags %}
<a href="{{ get_taxonomy_url(kind="tags", name=tag) }}">{{ tag }}</a>
{% endfor %}
</div>
{% endif %}
{{ page.content | safe }}
</article>
{% endblock %}
Filters and tests #
Tera provides filters (transform values) and tests (check conditions). Common ones:
{{ page.date | date(format="%B %Y") }} <!-- "January 2026" -->
{{ pages | slice(start=0, end=5) }} <!-- first 5 items -->
{{ count | pluralize }} <!-- "s" if count != 1 -->
{% if path is starting_with("/docs") %}...{% endif %}
See Tera’s documentation for the full list of filters and tests.
Blocks and inheritance #
Child templates extend base.html and override only the blocks they need.
Templates use block inheritance:
{# base.html #} <html> <body> {% block content %}{% endblock %} </body> </html>
{# page.html #} {% extends "base.html" %} {% block content %} <h1>{{ page.title }}</h1> {{ page.content | safe }} {% endblock %}
Discovering theme templates #
To see what templates and blocks a theme provides, check the theme source in the Zorto repository under crates/zorto-core/themes/<theme-name>/templates/. Each theme defines the same set of templates with different styling.
Macros #
Define reusable template fragments:
{% macro card(title, url) %}
<a href="{{ url }}" class="card">{{ title }}</a>
{% endmacro %}
{{ self::card(title="Home", url="/") }}
Further reading #
- Themes — how the theme system provides and layers templates
- Content model — how sections and pages map to templates
- How to customize your theme — step-by-step template overrides
- Tera documentation — full syntax reference