Handlebars guide
Frontmail templates use Handlebars syntax in the HTML body, text version, subject and recipient fields. Frontmail runs Handlebars in an isolated, locked-down environment: only the helpers listed on this page exist, partials are not available and every value is escaped.
Variables
Section titled “Variables”<p>Hello {{name}},</p><p>Your booking for {{booking.date}} is confirmed.</p> {{!-- nested object --}}<p>{{default company "(no company)"}}</p> {{!-- fallback --}}A missing value renders as an empty string. Use {{!-- … --}} for comments that never reach
the email.
Conditions
Section titled “Conditions”{{#if newsletter}} <p>Thanks for subscribing!</p>{{else}} <p>You can subscribe any time.</p>{{/if}}
{{#unless paid}}<p>Payment is pending.</p>{{/unless}}
{{#if (eq topic "support")}}<p>Our support team will reply within a day.</p>{{/if}}{{#if (and vip (gt total 1000))}}<p>🎁 Free shipping</p>{{/if}}{{#if (or (eq country "CZ") (eq country "SK"))}}<p>Doprava zdarma</p>{{/if}}{{#if}} treats false, 0, "", null, missing values and empty lists as false.
Use a list param with itemFields:
<table> {{#each items}} <tr> <td>{{@index}}</td> <td>{{title}}</td> <td>{{qty}} × {{formatMoney price ../currency}}</td> </tr> {{else}} <tr><td colspan="3">No items.</td></tr> {{/each}}</table>Inside {{#each}}, this is the current item, @index (0-based), @first and @last are
available, and ../ reaches the parent scope. {{#with customer}}…{{/with}} changes scope and
{{lookup map key}} reads a dynamic key.
Helpers
Section titled “Helpers”| Helper | Example | Result |
|---|---|---|
formatDate value [style] [timeZone=…] [locale=…] |
{{formatDate arrival "long"}} |
October 1, 2026 / 1. října 2026 |
formatNumber value [decimals=n] |
{{formatNumber 1234.5 decimals=2}} |
1,234.50 / 1 234,50 |
formatMoney amount currency [minor=true] |
{{formatMoney 49 "EUR"}} · {{formatMoney 4900 "EUR" minor=true}} |
€49.00 / 49,00 € |
uppercase value |
{{uppercase code}} |
ABC123 |
lowercase value |
{{lowercase email}} |
jana@example.com |
default value fallback… |
{{default nickname name "friend"}} |
first non-empty value |
eq a b · ne a b |
{{#if (eq status "paid")}} |
loose equality (1 equals "1") |
gt a b · lt a b |
{{#if (gt qty 10)}} |
numeric comparison |
and a b … · or a b … · not a |
{{#if (and a (not b))}} |
booleans (empty values are false) |
json value |
{{json items}} |
JSON text (escaped) – handy for debugging |
nl2br value |
{{nl2br message}} |
escaped text with line breaks turned into <br> |
formatDate styles: date, datetime, time, short, long, full, iso. The output
language follows the template locale; override it per call with locale="cs". Times use the
organization time zone unless you pass timeZone="Europe/Prague".
Calling any other helper is a compile error, shown in the template checks.
System variables
Section titled “System variables”| Variable | Value |
|---|---|
{{frontmail.message_id}} |
The message ID, e.g. msg_01J9Z3K7Q2 – useful as a reference number. |
{{frontmail.sent_at}} |
ISO timestamp of rendering, e.g. 2026-09-24T10:11:05.000Z. Combine with formatDate. |
{{frontmail.origin}} |
The Origin of the browser request (e.g. https://example.com), empty for server-side sends. |
frontmail is reserved – you cannot define a param with that name.
Escaping and the html type
Section titled “Escaping and the html type”Frontmail HTML-escapes every output, including {{{triple}}} and {{& ampersand}} forms.
A visitor typing <a href="…">click</a> into your form will see exactly that text in the email,
not a link.
When you genuinely need markup from params (for example rich text from your own CMS), declare the
param with type html. Its value is sanitized with an allowlist:
- tags:
a,b,strong,i,em,u,s,p,br,div,span,h1–h6,ul,ol,li,blockquote,pre,code,hr,img,tableand its parts,small,sub,sup,center,font; - links:
http,https,mailto,tel; images:http,https,cid; - inline styles: colors, font weight/style/size, line height, text alignment/decoration, margin, padding, width, height, max-width;
- everything else (scripts, iframes, forms, event handlers,
javascript:URLs) is removed.
Render it with plain {{body_html}} – the sanitized value is output as markup.
Text version
Section titled “Text version”The auto-generated text version is produced from the rendered HTML (links become text (url),
tables become lines). If you edit the text version by hand, it is rendered with the same
Handlebars rules, but without HTML escaping.