Guide
Template Collection & Conditional Helpers
Since v1.8.3. SSG's Go template engine ships a set of generic helpers for filtering, sorting, grouping, slicing and testing content — so a theme can build "recently updated guides", "related posts" or "grouped archives" without any code changes.
The collection is always the final argument, so helpers chain naturally in Go template pipelines:
{{ $recentGuides := .Site.Pages
| where "Type" "guide"
| sort "Modified" "desc"
| first 5
}}
{{ range $recentGuides }}
<article>
<h2><a href="{{ .URL }}">{{ .Title }}</a></h2>
<time>{{ formatDate .Modified }}</time>
</article>
{{ end }}
Helpers work on []models.Page and generically on slices of structs,
pointers to structs, string-keyed maps, and primitives (via reflection). They
never mutate their input and never panic — invalid usage stops template
execution with a descriptive error such as:
where: field "ModifiedAt" does not exist on models.Page
sort: unsupported direction "newest"; expected "asc" or "desc"
first: count must be greater than or equal to zero
matches: invalid regular expression "["
filter: unsupported operator "newest"; expected one of eq, ne, gt, ge, lt, le, …
What you do NOT need helpers for
if, else if, with, range, eq/ne/lt/le/gt/ge, and/or/not are native
Go template features — use them directly:
{{ if .Page.HasMath }}math{{ else if eq .Page.Type "guide" }}guide{{ else }}other{{ end }}
Native switch/case does not exist in Go templates and SSG deliberately
does not emulate it — compose if / else if with the conditional helpers below.
Supported comparison types
where, filter, sort and the equality-based helpers compare: strings,
booleans (false < true), all integer/float kinds (compared cross-type,
so int(5) equals float64(5)), time.Time (and convertible aliases).
Anything else (slices, maps, structs) errors in ordering contexts; equality
falls back to deep comparison.
Collection helpers
where — filter by field equality
{{ .Site.Pages | where "Type" "guide" }}
{{ .Site.Pages | where "Status" "publish" }}
{{ .Site.Pages | where "HasMath" false }}
Signature where(field, expected, collection). Matches struct fields,
pointer-to-struct fields and map keys exactly; preserves input order and element
type. A missing field/key is an error (no silent fallback).
filter — filter with an operator
{{ .Site.Pages | filter "Modified" "gt" $cutoff }}
{{ .Site.Pages | filter "Tags" "contains" "go" }}
{{ .Site.Pages | filter "Type" "in" (slice "guide" "tutorial") }}
Signature filter(field, operator, expected, collection). Operators:
eq ne gt ge lt le contains notContains in notIn.
contains searches strings (substring) and slices/arrays (element);
in/notIn test the field value against a provided collection.
sort — stable sort by field
{{ .Site.Pages | sort "Modified" "desc" }}
{{ .Site.Pages | sort "Title" "asc" }}
Signature sort(field, direction, collection); direction is asc or desc.
Stable, non-mutating (returns a sorted copy). Field must exist on every element
and hold a comparable type.
first / last / limit / offset — pagination
{{ .Site.Pages | first 5 }}
{{ .Site.Pages | last 3 }}
{{ .Site.Pages | sort "Modified" "desc" | limit 5 }} {{/* limit = first */}}
{{ .Site.Pages | offset 10 | limit 10 }} {{/* page 2 of 10 */}}
Negative counts error; counts past the end clamp (offset past the end yields
an empty collection); inputs are never mutated.
groupBy — group by scalar field
{{ range $category, $pages := (.Site.Pages | groupBy "Category") }}
<h2>{{ $category }}</h2>
{{ range $pages }}…{{ end }}
{{ end }}
Returns map[key] → slice. Item order inside each group is preserved. Go
templates iterate maps in sorted key order, so output is deterministic.
Keys must be scalar (string/bool/number/time.Time → RFC 3339); slices, maps
and other structs error.
uniq / uniqBy — deduplicate
{{ .Site.Pages | pluck "Category" | uniq }} {{/* primitives only */}}
{{ .Site.Pages | uniqBy "Category" }} {{/* structs, by field */}}
First occurrence wins. uniq on structs/maps errors — use uniqBy.
reverse — reversed copy
{{ .Site.Pages | reverse }}
slice — build a list inline
{{ slice "guide" "tutorial" "docs" }}
{{ if in .Page.Type (slice "guide" "tutorial") }}…{{ end }}
⚠️ Registering
sliceoverrides Go's builtinslice(value, i, j)sub-slicing function. Bundled themes do not use the builtin; if yours does, switch toprintf "%.10s"for strings or restructure the data.
pluck — extract one field
{{ $titles := .Site.Pages | pluck "Title" }}
Returns a []any of field values (nil for nil-pointer elements).
indexBy — build a lookup map
{{ $bySlug := .Site.Pages | indexBy "Slug" }}
{{ $page := index $bySlug "getting-started" }}
Duplicate keys and empty keys are errors (no silent overwrites).
Conditional helpers
| Helper | Signature | Example |
|---|---|---|
in |
in value collection → bool |
{{ if in .Page.Type (slice "guide" "docs") }} |
notIn |
notIn value collection → bool |
{{ if notIn .Page.Type (slice "draft") }} |
contains |
contains container value → bool |
{{ if contains .Page.Tags "ssg" }} — string→substring, slice→element, map→key |
startsWith |
startsWith value prefix → bool |
{{ if startsWith .Page.Slug "guide-" }} |
endsWith |
endsWith value suffix → bool |
{{ if endsWith .Page.SourceFile ".md" }} |
hasPrefix |
Hugo-compatible alias of startsWith (v1.8.5) |
{{ if hasPrefix .Page.Slug "guide-" }} |
hasSuffix |
Hugo-compatible alias of endsWith (v1.8.5) |
{{ if hasSuffix .Page.SourceFile ".md" }} |
matches |
matches pattern value → bool |
{{ if matches `^guide-` .Page.Slug }} — RE2; compiled patterns are cached; invalid patterns error |
isNil |
isNil value → bool |
true for nil interfaces/pointers/maps/slices/funcs/chans; never panics |
isEmpty |
isEmpty value → bool |
Go template truthiness: nil, "", 0, false, empty slice/map ⇒ empty. Structs are never empty (zero time.Time included — use .IsZero) |
ternary |
ternary cond a b → any |
{{ ternary .Page.HasMath "math" "plain" }} — for values, not control flow |
in takes the value first, collection second (the canonical form above).
For pipeline-style membership tests use filter … "in" … instead.
Content helpers (wrappers)
| Helper | Equivalent to | Example |
|---|---|---|
latest field n c |
sort field "desc" | first n |
{{ .Site.Posts | latest "Modified" 5 }} |
published c |
where "Status" "publish" |
{{ .Site.Pages | published }} |
byTag t c |
filter "Tags" "contains" t |
{{ .Site.Posts | byTag "go" }} |
byCategory name c |
(site-aware) | {{ .Site.Posts | byCategory "guides" }} — matches frontmatter Category or resolved category names/slugs, case-insensitive; []models.Page only |
byAuthor a c |
(site-aware) | {{ .Site.Posts | byAuthor "jan-kowalski" }} — by ID, name or slug; []models.Page only |
related page n c |
(scored) | {{ .Site.Posts | related .Page 3 }} — ranks by shared tags (3) > shared categories (2) > same author (1), recency breaks ties, excludes the current page, only positive scores |
Classic utility helpers
SSG registers several helper functions in the Go template engine for date formatting, HTML cleaning, metadata lookup, and logic controls.
HTML and String Utilities
safeHTML value— Returnstemplate.HTMLto prevent the Go template engine from auto-escaping HTML. Necessary when rendering custom templates or shortcode outputs.{{ .Content | safeHTML }}decodeHTML value— Unescapes standard HTML entity sequences (e.g.&becomes&).{{ decodeHTML .Title }}stripHTML value— Strips all HTML tags (<...>pattern) from the string.{{ .Content | stripHTML }}stripShortcodes value— Strips YouTube and embed WordPress-style bracket shortcodes ([youtube]...[/youtube],[embed]...[/embed]) from the text.{{ .Content | stripShortcodes }}
Date Formatting
formatDate value— Formats a date. If a string is passed, it returns it as-is.{{ formatDate .Date }}formatDatePL date— Formats a Gotime.Timedate using Polish month names (e.g.,14 lipca 2026).{{ formatDatePL .Date }}
Taxonomy and Metadata Lookup
getCategoryName id— Looks up and returns the name of a category by its integer ID frommetadata.json.{{ getCategoryName .Category }}getCategorySlug id— Looks up and returns the slug of a category by its integer ID frommetadata.json.{{ getCategorySlug .Category }}isValidCategory id— Returnstrueif the category ID is not1(ID1is commonly reserved for "Bez kategorii").{{ if isValidCategory .Category }}...{{ end }}getAuthorName id— Looks up and returns the name of an author by their integer ID frommetadata.json.{{ getAuthorName .Author }}hasValidCategories page— Returnstrueif the page or post has categories assigned other than ID1.{{ if hasValidCategories . }}...{{ end }}
Pages and URLs
getURL page— Helper function that returns the calculated URL path for the page or post. Equivalent to calling.GetURL.{{ getURL . }}getCanonical page— Helper function that returns the full canonical URL for the page or post. Equivalent to calling.GetCanonical .Domain.{{ getCanonical . }}
Miscellaneous Helpers
thumbnailFromYoutube value— Extracts the YouTube video ID from a YouTube WordPress-style shortcode and returns its high-quality video thumbnail URL (https://img.youtube.com/vi/<id>/hqdefault.jpg).{{ thumbnailFromYoutube .Content }}recentPosts n— Returns a list of the firstnposts. Safely clamps the count at both ends to prevent slice panics.{{ range recentPosts 5 }}...{{ end }}default defaultVal val— ReturnsdefaultValifvalis empty,nil,"", or0. Otherwise returnsval.{{ default "No title" .Title }}dict key1 val1 key2 val2 ...— Creates a dictionary (map) from key-value arguments. Useful for passing complex parameters into other helpers likeimageResize.{{ $image := imageResize "photo.jpg" (dict "width" 300 "height" 200 "mode" "fill") }}dictis also how a partial receives a computed context:{{ template "site-head" (dict "Title" .Page.Title "Ctx" .) }}
Arithmetic
Go templates have no arithmetic of their own; these four cover the cases a theme actually needs (column splits, "page N of M", index offsets).
add a b,sub a b,mul a b,div a b— Integer operands give an integer result anddivdivides integrally (div 7 2→3); a float operand anywhere gives a float (div 7.0 2→3.5). Dividing by zero is a template error rather than infinity, and a non-numeric argument fails the render with a message naming the helper.{{ $half := div (add (len .Site.Pages) 1) 2 }} {{ range $i, $p := .Site.Pages }}{{ if lt $i $half }}…{{ end }}{{ end }}
Availability
- Theme templates (
base/index/post/page/category.html, layouts, partials): every helper above. - Shortcode templates: the safe, deterministic subset —
slice,in,notIn,contains,startsWith,endsWith,hasPrefix,hasSuffix,matches,isNil,isEmpty,ternary— plus the image helpers (imageResize,imageSrcSet, …) and the read-only external-source helpers (getExternal,getExternalMeta). Collection helpers that walk site-wide data stay theme-only. - Alt engines (pongo2/mustache/handlebars): not applicable — those engines ship their own filter syntax; these helpers are Go-template only.
Limitations
- No custom predicates/lambdas, no
switch/case, no JS expressions (by design). sort/filterordering requires comparable field types (see above).groupBy/indexBy/uniqkeys must be scalar.uniqacross mixed numeric types dedupes by rendered value (1≡uint8(1)).