Templating allows you to render dynamic content like variables, conditionals, and loops within your Markdown or HTML content. jaspr_content provides built-in support for popular templating languages, enabling you to create rich, data-driven pages without much effort.
Templating is particularly useful for:
- Displaying data from frontmatter or external sources.
- Creating dynamic lists or tables.
- Showing content conditionally.
- Referencing other pages (with eager loading)
jaspr_content currently supports two templating languages out of the box:
- Mustache: A simple, logic-less template engine that focuses on variable substitution and basic conditions.
- Liquid: A more powerful template engine originally created by Shopify with support for complex logic, filters, and control flow.
Template engines can access data from various sources:
All frontmatter data from the current page is available under the page key:
---
title: My Page Title
author: Kilian Schulte
date: 2025-08-01
---
# {{page.title}}
Written by {{page.author}} on {{page.date}}Jaspr Content provides some built-in variables for each page:
page.path- The file path of the current pagepage.url- The URL of the current pagepages- An array of all pages (when using eager loading)
Data loaded from .yaml or .json files in your /content/_data directory is available by the filename:
# content/_data/site.yaml
title: My Awesome Website
description: A website built with Jaspr Content
author: Kilian Schulte# Welcome to {{site.title}}
{{site.description}} by {{site.author}}For nested data paths like content/_data/company/info.json, access it with dot notation: {{company.info.property}}.
For more information on data loading read the Data Loading documentation.
Mustache is a "logic-less" templating language - it focuses on simple variable substitutions and iterations rather than complex logic.
To enable Mustache templating, add the MustacheTemplateEngine to your page configuration:
ContentApp(
// ...
templateEngine: MustacheTemplateEngine(),
);This uses the mustache_template package.
Hello {{page.name}}!
{{#projects}}
<h3>{{name}}</h3>
<p>{{description}}</p>
{{/projects}}
{{^projects}}
No projects found.
{{/projects}}You can customize the MustacheTemplateEngine by providing options when creating it:
ContentApp(
// ...
templateEngine: MustacheTemplateEngine(
// Customize delimiters (default is "{{ }}")
delimiters: '{% %}',
// Change where partials are loaded from (default is "content/_partials/")
partialsRoot: 'content/_includes/',
// Customize the values available in templates
prepareValues: (page, pages) => {
...page.data,
'custom': 'value',
'currentYear': DateTime.now().year,
// Only include published pages
'pages': pages
.where((p) => p.data['page']['published'] == true)
.map((p) => p.data['page'])
.toList(),
},
),
);Liquid is a more powerful templating language with additional features like filters, control flow statements, and assignments.
To enable Liquid templating, add the LiquidTemplateEngine to your page configuration:
ContentApp(
// ...
templateEngine: LiquidTemplateEngine(),
);This uses the liquify package.
Hello {{ page.name | upcase }}!{% if page.show_banner %}
<div class="banner">Special Announcement</div>
{% endif %}
{% for member in team %}
<h3>{{ member.name }}</h3>
<p>{{ member.bio }}</p>
{% endfor %}{% assign greeting = "Hello" %}
{{ greeting }}, {{ page.name }}!You can customize the Liquid template engine by providing options:
ContentApp(
// ...
templateEngine: LiquidTemplateEngine(
// Change where includes are loaded from
includesPath: 'content/_includes/',
// Customize the template context or add custom tags/filters
prepareTemplate: (template, page, pages) {
// Add custom filters
template.registerFilter('greet', (input) => 'Hello, $input!');
// Add custom tags
template.registerTag('current_time', (_) async =>
DateTime.now().toIso8601String()
);
},
),
);Both template engines support including partial template files, which helps with reusability.
{{> header.md }}
# Page Content
{{> footer.md }}By default, these partials are loaded from content/_partials/.
{% include 'header.liquid' %}
# Page Content
{% include 'footer.liquid' %}By default, these includes are loaded from content/_includes/.
You can create your own templating implementation by implementing the TemplateEngine interface. This allows you to support other templating languages or even do custom preprocessing of content.
class CustomTemplateEngine implements TemplateEngine {
@override
Future<void> render(Page page, List<Page> pages) async {
// 1. Access page content with page.content
final content = page.content;
// 2. Process the content with your templating logic
final processedContent = myTemplateProcessingFunction(content, page.data, pages);
// 3. Update the page content
page.apply(content: processedContent);
}
}Then use it in your ContentApp:
ContentApp(
// ...
templateEngine: CustomTemplateEngine(),
);
