Templates
Your blocks are just HTML
How Templating Works
If you understand these three things, you understand 90% of templating.
{{variable}}prints a field{{#if}}hides or shows markup{{#each}}repeats markup
That's it. You write normal HTML. Handlebars decides whether it shows up and how many times.
<div>
<!-- Print a field -->
<h2>{{headline}}</h2>
<!-- Show markup conditionally -->
{{#if description}}
<p>{{description}}</p>
{{/if}}
<!-- Repeat markup for each item -->
{{#each items}}
<div>{{name}}</div>
{{/each}}
</div>
Displaying Data
Field values print into your markup. Variables, group fields, and rich text each output a little differently.
Variables
Field names become variables. A field named "Headline" becomes {{headline}}. Automatic kebab-case conversion. See Fields for details.
<h1>{{headline}}</h1>
<p>{{description}}</p>
<img src="{{image}}" alt="{{image.alt}}">
<a href="{{cta-url}}">{{cta-text}}</a>
Group Fields
Use dot notation for grouped fields.
{{settings.background-color}}
{{author.name}}
{{metadata.publish-date}}
WYSIWYG Fields
WYSIWYG fields return HTML. Use triple braces.
<!-- Wrong - HTML will be escaped -->
<div class="prose">{{content}}</div>
<!-- Right - HTML renders properly -->
<div class="prose">{{{content}}}</div>
Security note. Triple braces render HTML without escaping. Only use them for WYSIWYG fields where you trust the content source.
Conditionals
If/Else
{{#if description}}
<p>{{description}}</p>
{{/if}}
{{#if featured-image}}
<img src="{{featured-image}}" alt="{{headline}}">
{{else}}
<div>No image</div>
{{/if}}
Switch
{{#switch plan}}
{{#case "free"}}
<span>Free</span>
{{/case}}
{{#case "pro" "team"}}
<span>Paid</span>
{{/case}}
{{#otherwise}}
<span>Unknown plan</span>
{{/otherwise}}
{{/switch}}
Is Equal
There's also eq, the subexpression form, for use inside {{#if}} or another helper: {{#if (eq status "published")}}…{{/if}}.
{{#isEqual status "published"}}
<span>Published</span>
{{else}}
<span>Draft</span>
{{/isEqual}}
<!-- Number fields store strings, so compare against a quoted value -->
{{#isEqual count "0"}}
<p>No items</p>
{{else}}
<p>{{count}} items</p>
{{/isEqual}}
Displaying Lists
The items field generates arrays (lists) of objects with properties. Data stores also generate this structure.
Loop
Repeat markup for each item in a list.
<!-- Headline and description are unique for each item -->
{{#each items}}
<div>
<h3>{{headline}}</h3>
<p>{{description}}</p>
</div>
{{/each}}
<!-- Access the loop index -->
{{#each items}}
<div class="item-{{@index}}">
<span>#{{@index}}</span>
</div>
{{/each}}
First and Last
Only the first or last item.
{{#with (first items)}}
<h2>Just the first item</h2>
{{/with}}
{{#with (last items)}}
<h2>Just the last item</h2>
{{/with}}
Length
Get the length of the list of items.
<p>Showing {{length items}} items</p>
{{#if (length items)}}
<h2>We have {{length items}} items</h2>
{{/if}}
{{#isEqual (length items) 2}}
<h2>We have two items</h2>
{{/isEqual}}
Debugging
Not sure what data you have? Output it. The JSON helper shows you the exact structure of your data.
<!-- See everything available -->
<pre>{{json this}}</pre>
<!-- Check a specific field -->
<pre>{{json field-name}}</pre>
<!-- Debug an Items field -->
<pre>{{json features}}</pre>
Preview-only. {{json}} works in the editor preview, but there's no server-side json helper. Leave it in and the deployed block fails to compile, showing a "Template compilation error" in its place. Pull it out before you publish.
Layouts
Layouts wrap pages with shared structure: headers, footers, and navigation.
Every page has one layout. Layouts have two sections: header blocks (top) and footer blocks (bottom). Page content renders between them. Editing the layout updates every page that uses it.
The layouts editor has a CSS field so you can add styles that load with a specific layout. Use this to modify the structure. You can adjust where the header, footer and main content appear. It doesn't need to be in the traditional stacked arrangement.
<style>
body {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100%;
}
/* The content between the header and footer */
main {
flex:1;
}
/* Wraps for header and footer blocks */
div[data-section="header"] {}
div[data-section="footer"] {}
</style>
Layouts have a custom CSS section that will apply only to pages using that layout.
Quick Fixes
<!-- Variable doesn't match field name -->
{{heroHeadline}} <!-- ✗ Incorrect casing -->
{{hero-headline}} <!-- ✓ Field names are kebab-case -->
<!-- WYSIWYG needs triple braces -->
{{content}} <!-- ✗ Shows escaped HTML -->
{{{content}}} <!-- ✓ Renders HTML -->
<!-- Partial name must match exactly -->
{{> button}} <!-- ✗ Case matters -->
{{> Button}} <!-- ✓ Matches partial name -->
<!-- Loop not iterating? Check if data exists -->
{{#if items}}
<p>Found {{length items}} items</p>
{{else}}
<p>No items - check field name and data</p>
{{/if}}
<!-- Missing closing tag -->
{{#if condition}}
Content
<!-- Incorrect closing tag -->
{{#each items}}
{{headline}}
{{/if}}