Templates

Templates define the HTML structure for rendered pages using the Tera template engine.

Template Location

Templates are stored in the templates/ directory:

templates/
├── base.html              # Base template with common structure
├── page.html              # Single page template
├── section.html           # Section/list template (e.g., blog)
├── tags.html              # Tag listing page (all tags)
├── tags_term.html         # Individual tag page (e.g., /tags/rust/)
├── categories.html        # Category listing page (all categories)
├── categories_term.html   # Individual category page (e.g., /categories/tutorial/)
├── series.html            # Series listing page (all series)
├── series_term.html       # Individual series page (e.g., /series/learning-rust/)
└── 404.html               # Not found page

Template Engine

Taxus uses Tera, a Jinja2-like template engine for Rust:

  • Variables: {{ variable }} syntax
  • Filters: {{ content | safe }} for unescaped HTML
  • Conditionals: {% if condition %}...{% endif %}
  • Loops: {% for item in items %}...{% endfor %}
  • Template Inheritance: {% extends "base.html" %} and {% block name %}
  • Includes: {% include "partial.html" %}

Base Template

The base template defines the common HTML structure:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{% block title %}{{ site.name }}{% endblock %}</title>

    {% if page.permalink %}
    <link rel="canonical" href="{{ page.permalink }}" />
    <meta property="og:url" content="{{ page.permalink }}" />
    {% endif %}

    <link rel="stylesheet" href="/css/main.css" />
    <link rel="icon" href="/static/favicon.png" />
  </head>
  <body>
    <header>
      <nav>
        <a href="/">Home</a>
        <a href="/about/">About</a>
      </nav>
    </header>

    <main>{% block content %}{% endblock %}</main>

    <footer>
      <p>&copy; {{ now.year }} {{ site.author | default(value="") }}</p>
    </footer>

    <script src="/static/scripts.js"></script>
  </body>
</html>

Page Template

Page templates extend the base template:

{% extends "base.html" %}

{% block title %}{{ page.title }} - {{ site.name }}{% endblock %}

{% block content %}
<article>
  {% if page.hero %}
  <picture>
    <source srcset="{{ page.hero.srcset | safe }}" type="{{ page.hero.mime_type }}">
    <img src="{{ page.hero.src | safe }}" alt="{{ page.hero.alt }}"
         width="{{ page.hero.width }}" height="{{ page.hero.height }}"
         loading="eager" decoding="async">
  </picture>
  {% endif %}

  <h1>{{ page.title }}</h1>

  {% if page.description %}
  <p class="description">{{ page.description }}</p>
  {% endif %}

  {% if page.date %}
  <time datetime="{{ page.date }}">{{ page.date }}</time>
  {% endif %}

  {% if page.tags %}
  <div class="tags">
    {% for tag in page.tags %}
    <a href="/tags/{{ tag | slugify }}/">{{ tag }}</a>
    {% endfor %}
  </div>
  {% endif %}

  <div class="content">{{ page.content | safe }}</div>
</article>
{% endblock %}

Section Template

Section templates render lists of pages:

{% extends "base.html" %}

{% block title %}{{ section.title }} - {{ site.name }}{% endblock %}

{% block content %}
<section>
  <h1>{{ section.title }}</h1>

  {% if section.description %}
  <p class="description">{{ section.description }}</p>
  {% endif %}

  {% if section.content %}
  <div class="section-content">{{ section.content | safe }}</div>
  {% endif %}

  <ul class="page-list">
    {% for page in section.pages %}
    <li>
      <a href="{{ page.path }}">
        <span class="title">{{ page.title }}</span>
        {% if page.date %}
        <time datetime="{{ page.date }}">{{ page.date }}</time>
        {% endif %}
        <span class="reading-time">{{ page.reading_time }} min read</span>
      </a>
      {% if page.summary %}
      <p class="summary">{{ page.summary }}</p>
      {% endif %}
    </li>
    {% endfor %}
  </ul>

  {% if section.pagination %}
  <nav class="pagination">
    {% if section.pagination.prev %}
    <a href="{{ section.pagination.prev }}">← Previous</a>
    {% endif %}
    <span>Page {{ section.pagination.current }} of {{ section.pagination.total }}</span>
    {% if section.pagination.next %}
    <a href="{{ section.pagination.next }}">Next →</a>
    {% endif %}
  </nav>
  {% endif %}
</section>
{% endblock %}

Taxonomy Templates

Taxus generates taxonomy listing and term pages when the corresponding templates exist. The scaffold (taxus init) creates all six templates automatically.

Taxonomy List Templates

List templates render all terms for a taxonomy kind:

  • tags.html/tags/
  • categories.html/categories/
  • series.html/series/

Each receives extra.taxonomy with:

VariableTypeDescription
extra.taxonomy.kindStringTaxonomy kind: "Tags", "Categories", or "Series"
extra.taxonomy.pathStringURL path (e.g., "/tags/")
extra.taxonomy.termsArrayList of term contexts

Example tags.html:

{% extends "base.html" %}

{% block title %}Tags - {{ site.name }}{% endblock %}

{% block content %}
<section>
  <h1>Tags</h1>
  {% if extra.taxonomy.terms %}
  <ul>
    {% for term in extra.taxonomy.terms %}
    <li>
      <a href="{{ term.path }}">{{ term.name }} ({{ term.page_count }})</a>
    </li>
    {% endfor %}
  </ul>
  {% endif %}
</section>
{% endblock %}

Taxonomy Term Templates

Term templates render pages for a specific term:

  • tags_term.html/tags/rust/
  • categories_term.html/categories/tutorial/
  • series_term.html/series/learning-rust/

Each receives extra.taxonomy with:

VariableTypeDescription
extra.taxonomy.kindStringTaxonomy kind: "Tags", "Categories", or "Series"
extra.taxonomy.nameStringDisplay name (e.g., "Rust")
extra.taxonomy.slugStringURL-safe slug (e.g., "rust")
extra.taxonomy.pathStringURL path (e.g., "/tags/rust/")
extra.taxonomy.page_countNumberNumber of pages with this term
extra.taxonomy.pagesArrayList of page objects with title, path, description, etc.

Example tags_term.html:

{% extends "base.html" %}

{% block title %}Tag: {{ extra.taxonomy.name }} - {{ site.name }}{% endblock %}

{% block content %}
<section>
  <h1>Tagged "{{ extra.taxonomy.name }}"</h1>
  <p>{{ extra.taxonomy.page_count }} post(s)</p>
  <ul>
    {% for page in extra.taxonomy.pages %}
    <li><a href="{{ page.path }}">{{ page.title }}</a></li>
    {% endfor %}
  </ul>
</section>
{% endblock %}

Term Context in List Templates

When iterating extra.taxonomy.terms, each term has:

VariableTypeDescription
term.nameStringDisplay name
term.slugStringURL-safe slug
term.pathStringURL path
term.page_countNumberNumber of pages

Available Variables

Site Context

VariableTypeDescription
site.nameStringSite name from configuration
site.base_urlStringBase URL from configuration
site.descriptionString?Optional site description
site.authorString?Optional site author

Page Context

VariableTypeDescription
page.titleStringPage title from frontmatter
page.descriptionString?Optional page description
page.pathStringURL path (e.g., /about/)
page.permalinkStringAbsolute URL (e.g., https://example.com/about/)
page.contentStringRendered HTML content
page.raw_contentStringRaw markdown content
page.dateString?Publication date (ISO 8601)
page.draftBooleanWhether page is a draft
page.summaryStringSummary/excerpt for the page
page.word_countNumberWord count
page.reading_timeNumberEstimated reading time in minutes
page.tagsArrayTags for the page
page.categoriesArrayCategories for the page
page.seriesString?Series name
page.heroObject?Hero image context (see below)

Hero Image Context

When a page has hero_image in its frontmatter, page.hero contains:

VariableTypeDescription
page.hero.srcStringFallback <img> src (middle variant)
page.hero.srcsetStringFull srcset string for <source>
page.hero.widthNumberOriginal image width
page.hero.heightNumberOriginal image height
page.hero.altStringAlt text (from hero_alt, or page title)
page.hero.mime_typeStringMIME type (e.g., "image/webp")

Example usage:

{% if page.hero %}
<picture>
  <source srcset="{{ page.hero.srcset | safe }}" type="{{ page.hero.mime_type }}">
  <img src="{{ page.hero.src | safe }}" alt="{{ page.hero.alt }}"
       width="{{ page.hero.width }}" height="{{ page.hero.height }}"
       loading="eager" decoding="async">
</picture>
{% endif %}

See Images for the complete guide.

Section Context

VariableTypeDescription
section.titleStringSection title
section.descriptionString?Optional section description
section.pathStringSection URL path
section.contentString?Section HTML content
section.pagesArrayList of pages in section
section.paginationObject?Pagination information

Pagination Context

VariableTypeDescription
section.pagination.currentNumberCurrent page (1-indexed)
section.pagination.totalNumberTotal pages
section.pagination.per_pageNumberItems per page
section.pagination.total_itemsNumberTotal items across all pages
section.pagination.prevString?URL to previous page
section.pagination.nextString?URL to next page
section.pagination.firstStringURL to first page
section.pagination.lastStringURL to last page

Current Date

VariableTypeDescription
now.yearNumberCurrent year (e.g., 2024)

Useful for copyright notices: &copy; {{ now.year }}

Extra Variables

Custom variables from frontmatter extra field:

+++
title = "My Page"
[extra]
author = "John Doe"
custom = "value"
+++

Access in templates:

<p>Author: {{ extra.author }}</p>
<p>Custom: {{ extra.custom }}</p>

Template Inheritance

Templates can extend other templates:

base.html:

<html>
  <head>{% block head %}{% endblock %}</head>
  <body>{% block body %}{% endblock %}</body>
</html>

page.html:

{% extends "base.html" %}

{% block head %}
<title>{{ page.title }}</title>
{% endblock %}

{% block body %}
<h1>{{ page.title }}</h1>
{{ page.content | safe }}
{% endblock %}

Filters

Commonly used filters:

FilterDescription
safeOutput without HTML escaping
default(value="...")Provide default value
upperConvert to uppercase
lowerConvert to lowercase
trimRemove leading/trailing whitespace
firstGet first element of array
lastGet last element of array
lengthGet length of string/array
join(sep=", ")Join array with separator
slugifyConvert to URL-safe slug

Custom Templates

Pages can specify custom templates in frontmatter:

+++
title = "Special Page"
template = "custom.html"
+++

This page uses custom.html instead of page.html.

Island Components

Use the island() function to embed interactive Yew components:

{% block content %}
  {{ page.content | safe }}
  {{ island(component="Counter", initial=5) | safe }}
{% endblock %}

Important: Always use | safe after island() to prevent HTML escaping.

See Islands Architecture for the complete guide.