Template Syntax
Fast Mode uses a Handlebars-inspired template syntax to render dynamic CMS content. This guide covers all available tokens, loops, conditionals, and helpers.
Field Tokens
Basic Tokens (Escaped)
Use double braces for text fields. Output is HTML-escaped for security:
{{name}} <!-- Item name/title -->
{{slug}} <!-- URL-friendly identifier -->
{{url}} <!-- Full item URL (e.g., /blog/my-post) -->
{{publishedAt}} <!-- Formatted date (e.g., 1/15/2024) -->
{{createdAt}} <!-- Creation date -->
{{updatedAt}} <!-- Last modified date -->Raw HTML Tokens (Unescaped)
Use triple braces for rich text/HTML fields. This outputs raw HTML without escaping:
{{{content}}} <!-- Rich text field -->
{{{bio}}} <!-- Author biography -->
{{{description}}} <!-- Any HTML content -->Important: Always use triple braces for richText fields, or the HTML tags will display as text.
Nested/Related Field Access
Access fields from related items using dot notation:
<!-- On a blog post with an author relation field -->
{{author.name}} <!-- Author's name -->
{{author.picture}} <!-- Author's profile image -->
{{{author.bio}}} <!-- Author's bio (rich text) -->
{{author.url}} <!-- Link to author page -->
<!-- Wrap in conditional for safety -->
{{#if author}}
<span>By <a href="{{author.url}}">{{author.name}}</a></span>
{{/if}}Loops
Basic Loop
Loop through all items in a collection:
{{#each blog}}
<article>
<h2><a href="{{url}}">{{name}}</a></h2>
<p>{{excerpt}}</p>
<time>{{publishedAt}}</time>
</article>
{{/each}}Loop with Filters
Control which items display and in what order:
<!-- Limit to 3 most recent -->
{{#each blog limit=3 sort="publishedAt" order="desc"}}
<div>{{name}}</div>
{{/each}}
<!-- Skip first 3, show next 5 (pagination) -->
{{#each blog skip=3 limit=5}}
<div>{{name}}</div>
{{/each}}
<!-- Sort alphabetically -->
{{#each team sort="name" order="asc"}}
<div>{{name}}</div>
{{/each}}Available Filter Options
| Option | Description | Example |
|---|---|---|
limit=N | Show only N items | limit=3 |
skip=N | Skip first N items | skip=5 |
sort="field" | Sort by field name | sort="publishedAt" |
order="asc|desc" | Sort direction (default: desc) | order="asc" |
fieldName=true|false | Filter by boolean field | featured=true |
where="field:value" | Filter where field equals value | where="status:active" |
Filtering by Field Value
<!-- Show only featured posts -->
{{#each blog featured=true}}
<div class="featured">{{name}}</div>
{{/each}}
<!-- Filter by category -->
{{#each products where="category:electronics"}}
<div>{{name}} - {{price}}</div>
{{/each}}
<!-- Filter by nested/related field -->
{{#each blog where="author.slug:john-doe"}}
<div>{{name}}</div>
{{/each}}Loop Variables
Access metadata about the current loop iteration:
{{#each blog limit=5}}
<article class="{{#if @first}}first-post{{/if}} {{#if @last}}last-post{{/if}}">
<span class="position">#{{@index}}</span>
<h2>{{name}}</h2>
</article>
{{/each}}| Variable | Description | Example Value |
|---|---|---|
{{@index}} | Current item index (0-based) | 0, 1, 2... |
{{@first}} | True if first item | true/false |
{{@last}} | True if last item | true/false |
{{@length}} | Total items in filtered loop | 5 |
Using Loop Variables in Conditionals
{{#each blog limit=6}}
{{#if @first}}
<!-- Featured large card for first item -->
<div class="hero-card">{{name}}</div>
{{else}}
<!-- Smaller cards for others -->
<div class="small-card">{{name}}</div>
{{/if}}
{{/each}}Nested Loops & Parent Context
Using @root for Collection Access
Inside a loop, use @root to access other collections:
{{#each doc_categories sort="order" order="asc"}}
<div class="category-section">
<h3>{{name}}</h3>
<ul>
{{#each @root.doc_pages where="category.slug:{{slug}}" sort="order" order="asc"}}
<li><a href="{{url}}">{{name}}</a></li>
{{/each}}
</ul>
</div>
{{/each}}Dynamic Filtering with Parent Context
Reference the outer loop's values inside inner loop filters:
<!-- On category page: show only posts in this category -->
{{#each @root.blog where="category.slug:{{slug}}" limit=6}}
<div>{{name}}</div>
{{/each}}
<!-- On author page: show only their posts -->
{{#each @root.blog where="author.slug:{{slug}}" sort="publishedAt" order="desc"}}
<article>
<h3><a href="{{url}}">{{name}}</a></h3>
</article>
{{/each}}Parent Context References (../)
Inside loops, access parent scope fields using ../:
{{#each blog}}
{{#if (eq author.slug ../slug)}}
<!-- This post belongs to the current author -->
<span class="highlight">Your Post</span>
{{/if}}
<h3>{{name}}</h3>
{{/each}}Conditionals
Basic If/Else
{{#if featuredImage}}
<img src="{{featuredImage}}" alt="{{name}}">
{{else}}
<div class="placeholder-image"></div>
{{/if}}Unless (Negative Conditional)
{{#unless published}}
<span class="draft-badge">Draft</span>
{{/unless}}Nested Conditionals
{{#if author}}
<div class="author-card">
{{#if author.picture}}
<img src="{{author.picture}}" alt="{{author.name}}">
{{/if}}
<span>{{author.name}}</span>
</div>
{{/if}}Checking Collection Existence
<!-- Show section only if collection has items -->
{{#if blog}}
<section class="blog-section">
{{#each blog limit=3}}
<div>{{name}}</div>
{{/each}}
</section>
{{else}}
<p>No blog posts yet.</p>
{{/if}}Equality & Comparison Helpers
String Equality (eq)
<!-- Compare field to literal string -->
{{#eq status "published"}}
<span class="badge-success">Published</span>
{{/eq}}
{{#eq type "featured"}}
<div class="featured-highlight">★ Featured</div>
{{/eq}}Comparing Two Fields
<!-- Compare two field values -->
{{#if (eq author.slug currentAuthor)}}
<span>This is your post</span>
{{/if}}
<!-- With parent context -->
{{#if (eq category.slug ../slug)}}
<span class="current-category">Current</span>
{{/if}}Numeric Comparisons
Compare numbers using lt, gt, lte, gte, ne:
<!-- Show only first 4 items with special styling -->
{{#each blog}}
{{#if (lt @index 4)}}
<div class="featured-post">{{name}}</div>
{{else}}
<div class="regular-post">{{name}}</div>
{{/if}}
{{/each}}
<!-- Skip first item -->
{{#each team}}
{{#if (gt @index 0)}}
<div>{{name}}</div>
{{/if}}
{{/each}}
<!-- Not equal -->
{{#if (ne status "draft")}}
<span>Visible</span>
{{/if}}| Helper | Description | Example |
|---|---|---|
eq | Equal to | (eq status "active") |
ne | Not equal to | (ne status "draft") |
lt | Less than | (lt @index 3) |
gt | Greater than | (gt price 100) |
lte | Less than or equal | (lte @index 4) |
gte | Greater than or equal | (gte quantity 1) |
Video Embeds
Automatically convert video URLs to responsive embeds:
<div class="video-container">
{{#videoEmbed videoUrl}}{{/videoEmbed}}
</div>Supported platforms: YouTube, Vimeo, Wistia, Loom
The helper generates a responsive 16:9 iframe wrapper automatically.
Special Tokens
Built-in Item Fields
| Token | Description |
|---|---|
{{name}} | Item name/title |
{{slug}} | URL-friendly identifier |
{{id}} | Unique item ID |
{{url}} | Full URL to item detail page |
{{publishedAt}} | Publication date (formatted) |
{{createdAt}} | Creation date (formatted) |
{{updatedAt}} | Last update date (formatted) |
Site-Level Tokens
{{site.site_name}} <!-- Site name from manifest -->
{{site.name}} <!-- Alternative site name -->Whitespace Handling
Templates allow flexible whitespace. These are equivalent:
{{name}}
{{ name }}
{{ name }}Common Patterns
Blog Index Page
<section class="blog-listing">
{{#each blog sort="publishedAt" order="desc"}}
<article class="post-card">
{{#if main_image}}
<img src="{{main_image}}" alt="{{name}}">
{{/if}}
<h2><a href="{{url}}">{{name}}</a></h2>
<p>{{excerpt}}</p>
{{#if author}}
<span class="author">By {{author.name}}</span>
{{/if}}
<time>{{publishedAt}}</time>
</article>
{{/each}}
</section>Blog Detail Page
<article class="blog-post">
<h1>{{name}}</h1>
{{#if main_image}}
<img src="{{main_image}}" alt="{{name}}" class="hero-image">
{{/if}}
{{#if author}}
<div class="author-info">
{{#if author.picture}}
<img src="{{author.picture}}" alt="{{author.name}}">
{{/if}}
<span>By <a href="{{author.url}}">{{author.name}}</a></span>
</div>
{{/if}}
<time>{{publishedAt}}</time>
<div class="content">
{{{post_body}}}
</div>
</article>Featured + Regular Items
<!-- Hero section with first post -->
{{#each blog limit=1}}
<div class="hero-post">
<h1><a href="{{url}}">{{name}}</a></h1>
{{{excerpt}}}
</div>
{{/each}}
<!-- Grid of remaining posts -->
<div class="post-grid">
{{#each blog skip=1 limit=6}}
<div class="post-card">
<h3><a href="{{url}}">{{name}}</a></h3>
</div>
{{/each}}
</div>Documentation Sidebar
<nav class="doc-sidebar">
{{#each doc_categories sort="order" order="asc"}}
<div class="category">
<h4>{{name}}</h4>
<ul>
{{#each @root.doc_pages where="category.slug:{{slug}}" sort="order" order="asc"}}
<li><a href="{{url}}">{{title}}</a></li>
{{/each}}
</ul>
</div>
{{/each}}
</nav>Troubleshooting
Content Not Showing
Rich text appears as raw HTML? Use triple braces:
{{{content}}}Loop not rendering? Check collection slug matches exactly
Conditional always false? Field might be empty string, not null
Common Mistakes
<!-- WRONG: Double braces for HTML content -->
{{content}}
<!-- CORRECT: Triple braces for HTML -->
{{{content}}}
<!-- WRONG: Missing closing tag -->
{{#if author}}
<span>{{author.name}}</span>
<!-- CORRECT: Always close blocks -->
{{#if author}}
<span>{{author.name}}</span>
{{/if}}
<!-- WRONG: Typo in collection name -->
{{#each blogs}} <!-- Should be 'blog' not 'blogs' -->
<!-- CORRECT: Use exact collection slug -->
{{#each blog}}