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 (00–99)25✓
%CCentury (year ÷ 100)20✓
%GISO 8601 week-based year2025✓
%gISO week-based year without century25✓
%mMonth number (01–12)08✓
%BFull month nameAugust✓
%b, %hAbbreviated month nameAug✓
%dDay of month, zero-padded (01–31)06✓
%eDay of month, space-padded· 6✓
%jDay of year (001–366)218✓
%AFull weekday nameWednesday✓
%aAbbreviated weekday nameWed✓
%uWeekday, Monday 1 … Sunday 73✓
%wWeekday, Sunday 0 … Saturday 63✓
%UWeek of year, Sunday first (00–53)31✓
%WWeek of year, Monday first (00–53)31✓
%VISO 8601 week of year (01–53)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 (00–23)14
%kHour, 24-hour, space-padded14
%IHour, 12-hour, zero-padded (01–12)02
%lHour, 12-hour, space-padded· 2
%MMinute (00–59)05
%SSecond (00–60)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 — %-d → 6 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✓