Skip to main content

Template helpers

The Handlebars and Go template engines provide built-in helpers. Mustache and static templates do not.

Live examples: the helpers_handlebars, helpers_go_template, and format_date templates in the Gallery.

What no helper provides is arithmetic or number formatting. Nothing here adds two values, multiplies a quantity by a price, or renders a number as currency: the helpers below format dates, embed files, and render markdown, and that is the whole set. Compute derived values before you send the data — see Template authoring.

The Go engine is the exception, and only for formatting: html/template ships Go's own built-ins, so printf "%.2f" is available there without a helper. See Formatting numbers.

format_time

Formats an ISO-8601 timestamp using a strftime pattern. It accepts every conversion in the specifier reference below.

{{! Handlebars }}
{{format_time "%Y-%m-%dT%H:%M:%S%z" from_time}}
{{format_time "%A %d %B, %y" "2014-01-01T00:00:00Z"}}
{{/* Go — positional or piped */}}
{{format_time "%Y-%m-%dT%H:%M:%S%z" .from_time}}
{{.from_time | format_time "%Y-%m-%dT%H:%M:%S%z"}}

Accepted input

The input must be a full RFC 3339 timestamp — a timezone is required, fractional seconds are optional (2020-12-12T12:12:12Z, 2020-12-12T15:12:12.5+03:00). Anything else — a bare YYYY-MM-DD date, a timestamp with a space instead of T, or a locale date like 12/31/2025 — is rejected. To format a date without a time, use format_date.

In Handlebars a bad input surfaces as a helper error; in Go it returns an error from the pipeline.

format_date

Formats an ISO 8601 calendar date (YYYY-MM-DD) using a strftime pattern. Use it for date fields that carry no time; for full timestamps use format_time.

{{! Handlebars }}
{{format_date "%A %d %B, %Y" "2014-01-01"}}
{{/* Go — positional or piped */}}
{{format_date "%d.%m.%Y" .issued_on}}
{{.issued_on | format_date "%d.%m.%Y"}}

The input must be a bare YYYY-MM-DD date (2025-12-12). Anything carrying a time part — an RFC 3339 timestamp, or a date with a trailing time — is rejected; pass those to format_time instead. This matches the JSON-schema date format, so a field declared "format": "date" in a template's schema.json is always a valid format_date input.

The pattern may use only the date conversions — the rows marked ✓ in the specifier reference below — each optionally with the %-/%# no-padding flag. Any time or zone specifier (%H, %z, …) or unknown conversion is rejected rather than emitting a meaningless 00:00:00, since a date has no time.

Conversion specifiers

Both helpers share strftime's conversion set. format_time accepts all of it; format_date accepts only the rows marked ✓ in the Date column (plus the %-/%# no-padding flag on any of them). The Example column shows the output for Wednesday, 6 August 2025, 14:05:06 in a +02:00 (CEST) zone.

SpecifierMeaningExampleDate
%YYear with century2025
%yYear without century (0099)25
%CCentury (year ÷ 100)20
%GISO 8601 week-based year2025
%gISO week-based year without century25
%mMonth number (0112)08
%BFull month nameAugust
%b, %hAbbreviated month nameAug
%dDay of month, zero-padded (0131)06
%eDay of month, space-padded· 6
%jDay of year (001366)218
%AFull weekday nameWednesday
%aAbbreviated weekday nameWed
%uWeekday, Monday 1 … Sunday 73
%wWeekday, Sunday 0 … Saturday 63
%UWeek of year, Sunday first (0053)31
%WWeek of year, Monday first (0053)31
%VISO 8601 week of year (0153)32
%DDate as %m/%d/%y08/06/25
%FDate as %Y-%m-%d2025-08-06
%vDate as %e-%b-%Y· 6-Aug-2025
%xLocale date (%m/%d/%y)08/06/25
%HHour, 24-hour, zero-padded (0023)14
%kHour, 24-hour, space-padded14
%IHour, 12-hour, zero-padded (0112)02
%lHour, 12-hour, space-padded· 2
%MMinute (0059)05
%SSecond (0060)06
%pAM/PMPM
%RTime as %H:%M14:05
%rTime as %I:%M:%S %p02:05:06 PM
%TTime as %H:%M:%S14:05:06
%XLocale time (%H:%M:%S)14:05:06
%cLocale date and timeWed Aug ·6 14:05:06 2025
%ZTime-zone nameCEST
%zTime-zone offset from UTC+0200
%nNewline\n
%tTab\t
%%Literal %%

A · marks a padding space. Any numeric conversion may take the glibc %- or Windows %# flag to drop that padding — %-d6 instead of 06. The flag is valid wherever its conversion is, so format_date takes %-d but still rejects %-H. There are no E/O locale modifiers; %c, %x, and %X use the C locale.

markdown

Renders a Markdown string to HTML and returns it as safe (unescaped) HTML.

{{markdown description}}
{{markdown .description}}
{{.description | markdown}}

Security: markdown output is not HTML-escaped, and the renderer does not strip embedded HTML. Never pass untrusted data to markdown without sanitising it. See Security model.

embed_text

Reads a file from the template's assets/ directory and inlines its text content — handy for inlining CSS.

<style>{{embed_text "styles.css"}}</style>

The content is inlined as-is, with double braces: escaping it would be destroying it, since a stylesheet whose quotes came back as &apos; no longer parses as CSS — the browser drops the declaration and paints the page in its own defaults, with nothing reported. Go templates escape by context, so there embed_text returns the type matching the file's extension (.css, .js, otherwise HTML) and it lands unescaped in the element it was written into.

Security: the same property that keeps the CSS intact means embed_text emits raw content. The path names a file the template author shipped — never build it from request data.

embed_base64

Reads an asset and returns its base64 encoding — handy for inlining images as data URIs.

<img src="data:image/svg+xml;base64,{{embed_base64 "logo.svg"}}">

Paths are relative to the template's assets/ directory.

equal (Handlebars only)

A block helper that renders its block when two stringified values are equal, with an {{else}} inverse.

{{#equal status "paid"}}
<span class="ok">Paid</span>
{{else}}
<span class="due">Due</span>
{{/equal}}

Helper availability

HelperHandlebarsGoMustacheStatic
format_time
format_date
markdown
embed_text
embed_base64
equal