Shortcodes

Shortcodes let authors use reusable presentation in Markdown without writing raw HTML. The Shortcodes reference documents every local shortcode; this page explains when and how to create one.

Built-ins are not in the local inventory#

Hugo provides built-in shortcodes outside layouts/shortcodes/, so they do not appear in the local Shortcodes inventory. The important one here is relref, which resolves an internal link at build time:

[Callouts]({{< relref "/docs/site-implementation/shortcodes/callouts" >}})

Prefer relref to a hand-typed URL for two reasons: it fails the build for a bad content path, and it follows the content path when permalink rules change or a section flattens directories.

Always give it an absolute content path. A bare filename resolves only until a file moves or a name collides.

Writing about shortcodes#

To display a shortcode without executing it, place /* and */ inside its braces:

{{< callout type="archive" >}}

This is Hugo parser syntax, not a shortcode, so it does not appear in the inventory. Use it in documentation, including every example on this page.

Reuse a partial from a shortcode#

Keep shared rendering in a partial and call that partial from a shortcode. The same HTML can then serve Markdown authors through the shortcode and layouts through the partial.

Create the partial#

Create layouts/partials/card.html:

<div class="card">
  <h3>{{ .title }}</h3>
  <p>{{ .description }}</p>
</div>

Create the shortcode#

Create layouts/shortcodes/card.html, read its named parameters, and pass only the partial contract:

{{ $title := .Get "title" }}
{{ $description := .Get "desc" }}

{{ partial "card.html" (dict "title" $title "description" $description) }}

Pass . when the partial needs the complete shortcode context, or pass .Inner when a wrapped shortcode needs its inner content. Keep the partial’s expected values explicit when a smaller contract is sufficient.

Call it from Markdown#

Authors can then use the shortcode in any Markdown file:

{{< card title="Hello World" desc="This content came from a partial called via a shortcode." >}}

Document the rendered contract#

Each record in data/shortcodes.yml has a documentation mode. Omit documentation_mode for the normal live mode and render an active invocation on the bilingual reference pages. Use documentation_mode: dependent with exactly one preview_parent or preview_page when the shortcode needs a parent composition or real page data; both reference pages must use component-relationship to point to that rendered example.

Use documentation_mode: nonvisual only when the interface has no independent rendered state. In that case, write localized Result and Constraints sections in both languages. Describe the output or transformation, the inputs it reads, and the boundary a caller must preserve. A decorative mock-up is not evidence for a non-visual contract.

npm run lint:interfaces verifies the mode, both translations, the active invocation or relationship target, and the required non-visual sections.

When not to create one#

Create a shortcode when the alternative repeats markup or when content needs site data, such as definition reading the glossary or emoji reading the emoji table.

Do not create one when Markdown already provides the right syntax. Callouts are the clearest example: moving from a shortcode-only form to the standard > [!NOTE] alert syntax means the same text works in Bear, GitHub, and this site. A shortcode creates a small site-specific dependency. Use one when it earns that cost.