Hugo templates combine Go’s template language with Hugo functions and page
objects. Shortcodes use the same language, but content authors invoke them from
Markdown while Hugo constructs .Content.
Follow Go Template Context and Scope #
Template actions appear inside {{ and }}. A pipeline passes the result of
each command to the final argument of the next:
{{ .Title | strings.TrimSpace | htmlEscape }}Dot (.) is the current context. with, range, and template calls can rebind
it, so capture an outer object before entering a narrower scope:
{{ $page := . }}
{{ range .Pages }}
<a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
<span>{{ $page.Site.Title }}</span>
{{ end }}Variables begin with $ and remain visible through the scope in which they are
declared. The special root variable $ initially refers to the context passed
to the template, but explicitly named variables make nested code easier to
review.
Go template functions return values that pipelines can transform. Some also
return an error as their final result; a non-nil error stops template
execution. Use with for optional values and errorf when absence violates a
content contract rather than silently emitting incomplete markup.
Preserve Contextual Escaping #
HTML layouts execute through Go’s html/template, which escapes values
according to whether they appear in text, an attribute, a URL, CSS, or
JavaScript:
<a href="{{ .Params.target }}">{{ .Params.label }}</a>Functions such as safeHTML, safeCSS, safeJS, and safeURL mark a value as
trusted for one context. They do not sanitize untrusted input. Apply them only
to content generated or validated by the site, because using the wrong safe
type can create a cross-site scripting path.
Hugo’s template.HTML-like safe values also cross a partial boundary. A
partial should therefore document whether it expects plain text, a URL, a page
object, or trusted markup.
Pass Explicit Data to Partials #
Partials under layouts/_partials/ render reusable template fragments. The
caller chooses their dot:
{{ partial "card.html" (dict
"page" .
"compact" true
) }}Inside card.html, .page and .compact are available, but the original page
is not implicitly restored. Explicit dictionaries prevent a partial from
depending on whichever dot happened to be active at its call site.
partialCached stores a partial’s result for the build:
{{ partialCached "menu.html" . .Language.Lang .Section }}Cache variants must include every input that can change the output. Without variant keys, page-specific content can be reused on unrelated pages.
Locate Shortcode Templates and Resources #
With current Hugo versions, project shortcode templates reside under
layouts/_shortcodes/; modules or themes can provide a lower-precedence
definition. Subdirectories form part of the shortcode name. A call to
docs/note resolves a template such as:
layouts/_shortcodes/docs/note.htmlThe invocation belongs in a content page, whether that page is a regular file
or a bundle’s index.md. Assets do not belong in the layout directory. A
shortcode used by a leaf bundle can resolve sibling resources through
.Page.Resources.Get, while site-wide assets come from the global resource
pipeline.
A shortcode receives a shortcode context rather than the surrounding page as
dot. Common members include .Page, .Params, .Get, .Inner,
.InnerDeindent, .Ordinal, and .Parent. Named and positional arguments
should not be mixed ambiguously:
{{ $src := .Get "src" }}
{{ with .Page.Resources.Get $src }}
<img src="{{ .RelPermalink }}" alt="{{ $.Get "alt" }}">
{{ else }}
{{ errorf "shortcode image %q not found in %s" $src $.Page.Path }}
{{ end }}Choose the Markdown Boundary #
Hugo provides two delimiter forms:
{{< note >}}inserts shortcode template output without sending that output through the Markdown renderer.{{% note %}}sends shortcode template output through Markdown rendering.
The percentage form is appropriate when the shortcode produces Markdown. The angle-bracket form is appropriate when the template owns the final HTML. Indentation still matters in the Markdown form because four spaces can turn generated content into a code block. Goldmark’s unsafe-HTML setting also affects raw HTML that enters the Markdown path.
For a paired shortcode, .Inner contains its inner content after Hugo resolves
nested shortcode calls. The shortcode template decides whether to render that
value directly or pass it through .Page.RenderString; do not render an
already-rendered value a second time.
Emit Literal Delimiters Safely #
Markdown code fences do not protect shortcode-looking text because shortcode expansion occurs before the Markdown renderer handles the fence. Hugo’s comment escape emits a literal example without attempting lookup:
{{< note >}}
{{% note %}}HTML entities are another option when the rendered page only needs to display the delimiters. Use the comment escape when readers should be able to copy the literal shortcode syntax from the rendered result.
Diagnose Lookup and Execution Failures #
An unknown shortcode normally reports the content file and shortcode name. Check, in order:
- The name and case used at the call site.
- The
_shortcodesdirectory and any subdirectory in the name. - The active module mounts and theme precedence.
- Whether the project version expects the current layout namespace.
- Whether template execution failed after lookup because an argument or resource was absent.
Use warnf for actionable diagnostics that may continue and errorf for
broken invariants that must fail the build. Templates provide site structure,
partials reuse structure, and shortcodes bridge author-controlled content to a
deliberately bounded template API.
Comments
Read or comment on GitHub (sign-in required to comment)