Templating
Use templating languages to create dynamic content.
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:
Frontmatter
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}}
Built-in Variables
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 Files
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 Templates
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.
Sections (Loops and Conditionals)
<!-- If projects exists and is not empty -->
{{#projects}}
<h3>{{name}}</h3>
<p>{{description}}</p>
{{/projects}}
<!-- Inverted sections (if not) -->
{{^projects}}
No projects found.
{{/projects}}
Customizing Mustache
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 Templates
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.
Control Flow
{% if page.show_banner %}
<div class="banner">Special Announcement</div>
{% endif %}
{% for member in team %}
<h3>{{ member.name }}</h3>
<p>{{ member.bio }}</p>
{% endfor %}
Customizing Liquid
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()
);
},
),
);
Including Template Files
Both template engines support including partial template files, which helps with reusability.
Mustache Partials
{{> header.md }}
# Page Content
{{> footer.md }}
By default, these partials are loaded from content/_partials/
.
Liquid Includes
{% include 'header.liquid' %}
# Page Content
{% include 'footer.liquid' %}
By default, these includes are loaded from content/_includes/
.
Creating Custom Template Engines
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(),
);