HTML Template Syntax
HTML templates use a simple templating language and data sources to generate HTML or other text formats. HTML templates look like regular text with embedded template expressions.
The example below shows how a data source may appear using the provided template:
<div> <p>Name: {{Name}} {{Surname}}</p> <p>Address: {{Address.Number}}, {{Address.Street}}, {{Address.City}}</p></div>{ "Title":"Profile", "Name":"John", "Surname":"Smith", "Address": { "Number":"200", "Street":"Austin rd.", "City":"Dallas" }}<div> <p>Name: John Smith</p> <p>Address: 200, Austin rd., Dallas</p></div>An expression consists of {{, some contents, then }}. When the template is executed, these expressions
are replaced with values from an input object.
Language Features
Simple Expressions
The following template defines two template expressions. If applied to the input object, the expressions will be replaced by the corresponding properties. The result is then:
<p>{{firstname}} {{lastname}}</p>{ "firstname": "Yehuda", "lastname": "Katz"}<p>Yehuda Katz</p>Nested Input Objects
Sometimes, the input object contains other objects or arrays. In such a case, you can use a dot-notation to gain access to the nested properties. For example:
{{Person.FirstName}} {{Person.LastName}}{ "Person": { "FirstName": "Yehuda", "LastName": "Katz" }}Yehuda KatzSome built-in helpers allow you to change the current context to a nested object. You can then access this object as if it were the root object.
Evaluation context
When working with templates, the “context” refers to the current scope of data available for evaluation.
The this keyword can be used to access the current context.
The current evaluation context can be changed through the use of different helpers, such as the with helper.
The with helper dives into an object, giving you access to its properties:
{{#with person}} {{firstname}} {{lastname}}{{/with}}{ "person": { "firstname": "Yehuda", "lastname": "Katz" }}Yehuda KatzYou can optionally provide an {{else}} section which will display only when the passed value is empty.
{{#with city}}{{city.name}} (not shown because there is no city){{else}}No city found{{/with}}{ "person": { "firstname": "Yehuda", "lastname": "Katz" },}No city foundHTML Escaping
Because it was originally designed to generate HTML, templates escape values returned by a {{expression}}. If you
don’t want to escape a value, use the “triple-stash”, {{{.
raw: {{{specialChars}}}html-escaped: {{specialChars}}{ "specialChars": "& < > \" ' ` =" }raw: & < > " ' ` =html-escaped: & < > " ' `Raw Block:
Raw Blocks are used to completely skip any template parsing for the content inside it.
Anything wrapped within the {{{{raw}}}}...{{{{/raw}}}} is produced in the output verbatim, exactly as written.
{{{{raw}}}}{{#each items}} <p>{{this}}</p>{{/each}}{{{{/raw}}}}{ "items": ["Apple", "Banana", "Cherry"]}{{#each items}} <p>{{this}}</p>{{/each}}Conditionals
You can use the if helper to conditionally render a block. If its argument returns false, undefined, null, "",
0, or [], the block will not be rendered.
<div> {{#if author}} <h1>{{firstName}} {{lastName}}</h1> {{/if}}</div>{ "author": true, "firstName": "Yehuda", "lastName": "Katz"}<div> <h1>Yehuda Katz</h1></div>If the input is an empty JSON object {}, then author will become undefined and if condition fails, resulting in
the output below:
<div></div>When using a block expression, you can specify a template section to run if the expression returns a falsy value. The
section, marked by else is called an “else section”.
<div> {{#if author}} <h1>{{firstName}} {{lastName}}</h1> {{else}} <h1>Unknown Author</h1> {{/if}}</div>{ "author": false, "firstName": "Yehuda", "lastName": "Katz"}<div> <h1>Unknown Author</h1></div>You can use the unless helper as the inverse of the if helper. Its block will be rendered if the expression returns
a falsy value.
<div> {{#unless license}} <h3 class="warning">WARNING: This entry does not have a license!</h3> {{/unless}}</div>{ "license": false}<div> <h3 class="warning">WARNING: This entry does not have a license!</h3></div>If looking up license under the current context returns a falsy value, the warning will be rendered. Otherwise,
it will render nothing.
Loops
You can iterate over a list using the built-in each helper. Inside the block, you can use this to reference the
element being iterated over.
<ul class="people_list"> {{#each people}} <li>{{this}}</li> {{/each}}</ul>{ "people": ["Yehuda Katz", "Alan Johnson", "Charles Jolley"]}<ul class="people_list"> <li>Yehuda Katz</li> <li>Alan Johnson</li> <li>Charles Jolley</li></ul>You can use the this expression in any context to reference the current context.
You can optionally provide an else section which will display only when the list is empty.
{{#times}} {{#person}} <p>Meeting with {{person}} at {{time}}.</p> {{else}} <p class="empty">No meeting at {{time}}.</p> {{/person}}{{/times}}{ "times": [ { "time": "13:00", "person": "Yehuda Katz" }, { "time": "14:00", "person": "Alan Johnson" }, { "time": "15:00", "person": null } ]}<p>Meeting with Yehuda Katz at 13:00.</p><p>Meeting with Alan Johnson at 14:00.</p><p class="empty">No meeting at 15:00.</p>When looping through items in each, you can optionally reference the current loop index via {{@index}}.
{{#each array}} {{@index}}: {{this}} {{/each}}Additionally for object iteration, {{@key}} references the current key name:
{{#each object}} {{@key}}: {{this}} {{/each}}It’s often useful to know when you’re on the first or last item in a loop.
The first and last steps of iteration are noted via the @first and @last variables when iterating over an array.
When iterating over an object only the @first is available. Nested each blocks may access the iteration variables via
depth based paths.
<ul> {{#each items}} <li> {{this}} {{#if @first}}<strong>(first)</strong>{{/if}} {{#if @last}}<strong>(last)</strong>{{/if}} </li> {{/each}}</ul>{ "items": ["Red", "Green", "Blue"]}- Red (first)
- Green
- Blue (last)
When in a loop, the parent index can be accessed with {{@../index}}.
{{#each teams as |team|}} <h3>{{team.name}}</h3> <ul> {{#each team.players}} <li>{{this}} plays for {{team.name}} (Team index {{@../index}})</li> {{/each}} </ul>{{/each}}{ "teams": [ { "name": "Red Dragons", "players": ["Alice", "Bob"] }, { "name": "Blue Whales", "players": ["Carol", "Dave", "Eve"] } ]}<h3>Red Dragons</h3><ul> <li>Alice plays for Red Dragons (Team index 0)</li> <li>Bob plays for Red Dragons (Team index 0)</li></ul>
<h3>Blue Whales</h3><ul> <li>Carol plays for Blue Whales (Team index 1)</li> <li>Dave plays for Blue Whales (Team index 1)</li> <li>Eve plays for Blue Whales (Team index 1)</li></ul>Similarly, the @root keyword will access the top-level context:
{{#each departments}} <h4>Department: {{name}}</h4> <p>Company: {{@root.companyName}}</p> <ul> {{#each employees}} <li>{{this}} works at {{@root.companyName}}</li> {{/each}} </ul>{{/each}}{ "companyName": "Globex Inc", "departments": [ { "name": "Sales", "employees": ["Alice", "Bob"] }, { "name": "Engineering", "employees": ["Carol", "Dave"] } ]}<h4>Department: Sales</h4><p>Company: Globex Inc</p><ul> <li>Alice works at Globex Inc</li> <li>Bob works at Globex Inc</li></ul>
<h4>Department: Engineering</h4><p>Company: Globex Inc</p><ul> <li>Carol works at Globex Inc</li> <li>Dave works at Globex Inc</li></ul>Lookup
The lookup helper allows for dynamic parameter resolution. It is useful for resolving values for array indexes.
It can also be used to lookup object properties based on data from the input.
{{#each people}} {{.}} lives in {{lookup ../cities @index}}{{/each}}{ "people": ["Alice", "Bob", "Charlie"], "cities": ["Paris", "London", "New York"]}Alice lives in ParisBob lives in LondonCharlie lives in New YorkIt can also be used to retrieve object properties using input data. In the following, a more advanced example demonstrates how a lookup within a sub-expression can shift the evaluation context to a different object, guided by a specific property-value:
{{#each persons as |person|}} {{person.name}} lives in {{#with (lookup ../cities person.resides-in) as |city|}} {{city.name}} ({{city.country}}) {{/with}}{{/each}}{ "persons": [ { "name": "Alice", "resides-in": "Paris" }, { "name": "Bob", "resides-in": "London" }, { "name": "Charlie", "resides-in": "New York" } ], "cities": { "Paris": { "name": "Paris", "country": "France" }, "London": { "name": "London", "country": "UK" }, "New York": { "name": "New York", "country": "USA" } }}Alice lives in Paris (France)Bob lives in London (UK)Charlie lives in New York (USA)Formatting Values
Dates and Times
Dates from a data source property can be formatted using the formatdate function.
{{formatdate createddate 'dd/MM/yyyy HH:mm:ss'}}{ "createddate": "2021-04-23T18:25:43.511Z"}23/04/2021 18:25:43Or to return the relative time use the friendly formatting option:
{{formatdate createddate 'friendly'}}{ "createddate": "2021-04-23T18:25:43.511Z"}in one monthThe following table shows the keys and sample outputs used by the friendly format:
| Range | Key | Sample Output |
|---|---|---|
| 0 to 44 seconds | s | in seconds |
| 45 to 89 seconds | m | in a minute |
| 90 seconds to 44 minutes | mm | in 2 minutes … in 44 minutes |
| 45 to 89 minutes | h | in an hour |
| 90 minutes to 21 hours | hh | in 2 hours … in 21 hours |
| 22 to 35 hours | d | in a day |
| 36 hours to 25 days | dd | in 2 days … in 25 days |
| 26 to 45 days | M | in a month |
| 45 to 319 days | MM | in 2 months … in 10 months |
| 320 to 547 days (1.5 years) | y | in a year |
| 548 days+ | yy | in 2 years … in 20 years |
Whitespace Control
Template whitespace may be omitted from either side of any expression statement by adding a ~ character by the braces. When applied all whitespace on that side will be removed up to the first expression or non-whitespace character on that side.
<nav aria-label="Main menu">{{#each menu~}} <a href="{{url}}" class="menu-link" aria-current="{{#if current}}page{{/if}}"> {{~title}} </a>{{~/each}}</nav>{ "menu": [ { "url": "/home", "title": "Home", "current": true }, { "url": "/about", "title": "About" }, { "url": "/contact", "title": "Contact" } ]}<nav aria-label="Main menu"><a href="/home" class="menu-link" aria-current="page">Home</a><a href="/about" class="menu-link" aria-current="">About</a><a href="/contact" class="menu-link" aria-current="">Contact</a></nav>