How to Write a Hugo Shortcode

Hugo Shortcodes are reusable snippets to insert dynamic or complex content.

Introduction

Shortcodes make it easy to reuse the same logic or markup across your content. They help keep Markdown files readable while allowing you to insert dynamic or configurable elements when needed.

You should consider writing a shortcode when:

  • You need to reuse the same component in multiple places
  • You want to centralize or share data across different content files
  • You need to encapsulate complex or verbose HTML
  • You want to ensure consistent presentation across posts
  • You need to embed third-party content (videos, tweets, code snippets, etc.)
  • You want to separate content from layout and styling concerns
  • You need conditional rendering based on parameters or context
  • You want to provide simple, author-friendly building blocks instead of raw HTML

The Basics

To call a shortcode in your post, use its filename (without .html).

  • Use {{% %}} when the shortcode output contains Markdown that must be rendered
  • Use {{< >}} when the shortcode outputs HTML only (preferred for performance)

Examples:

  • {{% parameters params.yaml %}} # Markdown-aware
  • {{< parameters "params.yaml" >}} # HTML-only
  • {{% hugo/parameters params.yaml %}} # will look for ./layouts/shortcodes/hugo/parameters.html

In this posts, we already saw that we can use shortcode provided by the theme’s modules, but let’s write some customs.

Some use cases

Split code from the articles

On this blog, I often include large code blocks. Instead of embedding them directly in Markdown, itโ€™s often cleaner to keep them next to the article - for example in a code/ or codes/ folder - and include them using a dedicated shortcode.

This approach makes the article easier to read and allows code snippets to be reused or updated independently of the content. Separating code from content keeps articles readable while letting code evolve independently. You can achieve this with a code-snippet shortcode.

  • Shortcode location: ./layouts/shortcodes/code-snippet.html

  • Content structure example:

1content/
2โ””โ”€ posts/
3   โ””โ”€ my-article/
4      โ”œโ”€ index.md
5      โ”œโ”€ codes/
6      โ”‚  โ””โ”€ example.conf
  • The code:
 1{{- $name := printf "{code/%s,codes/%s}" ($.Get 0) ($.Get 0) }}
 2{{- $res := .Page.Resources.GetMatch $name }}
 3
 4{{- with $res }}
 5  {{- /* 1. Explicit language argument */ -}}
 6  {{- $lang := $.Get 1 | default "" }}
 7
 8  {{- /* 2. Fallback to file extension */ -}}
 9  {{- if not $lang }}
10    {{- $lang = replaceRE "^.*\\." "" .Name | lower }}
11  {{- end }}
12
13  {{- /* 3. Final fallback */ -}}
14  {{- if not $lang }}
15    {{- $lang = "txt" }}
16  {{- end }}
17
18  {{- highlight .Content $lang "" }}
19{{- else }}
20  {{- warnf "code snippet not found: %s" $name }}
21{{- end }}
  • The usage: {{< code-snippet example.txt ini>}}

  • The result:

    1[Test]
    2Description="Some random text to demonstrate the `code-snippet` shortcode."

Generate a Table from a yaml

You can generate a table from a configuration file using a shortcode. This is especially useful when you need to apply custom styling or effects, or when the dataset is large.

Instead of maintaining a long Markdown table, you can store the data in a a-long-list-of-stuffs.yaml file, which is easier to read and update over time.

So let do a shortcode which:
โœ” Generate a valid Markdown table
โœ” Auto-generates headers
โœ” Makes URLs clickable
โœ” Renders Markdown / HTML when present
โœ” Supports YAML / JSON / TOML inputs

  • The code in ./layouts/shortcodes/table-snippet.html:
 1{{- $res := .Page.Resources.GetMatch (.Get 0) }}
 2{{- if not $res }}
 3  {{- errorf "table-snippet: file not found: %s" (.Get 0) }}
 4{{- end }}
 5
 6{{- $data := $res | transform.Unmarshal }}
 7
 8{{- /* Optional column order from shortcode */ -}}
 9{{- $headers := slice }}
10{{- with .Get 1 }}
11  {{- $headers = split . "," }}
12{{- end }}
13
14{{- /* Fallback: auto-detect keys if no order provided */ -}}
15{{- if eq (len $headers) 0 }}
16  {{- range $data }}
17    {{- range $k, $_ := . }}
18      {{- if not (in $headers $k) }}
19        {{- $headers = $headers | append $k }}
20      {{- end }}
21    {{- end }}
22  {{- end }}
23{{- end }}
24
25<table class="table table-bordered table-hover table-striped">
26  <thead>
27    <tr>
28      {{- range $headers }}
29        <th>{{ . }}</th>
30      {{- end }}
31    </tr>
32  </thead>
33  <tbody>
34    {{- range $data }}
35      {{- $row := . }}
36      <tr>
37        {{- range $headers }}
38          {{- $val := index $row . | default "" }}
39          <td>{{ printf "%v" $val | markdownify }}</td>
40        {{- end }}
41      </tr>
42    {{- end }}
43  </tbody>
44</table>
  • The usage: {{< table-snippet list.yaml "name,description" >}}

  • The result:

    namedescription
    Element 1A description of element 1.
    Element 2A description of element 2.
    Element 3A description of element 3.

NB: here I generate HTML output, so the right syntax to use it, is {{< >}}

Some ideas for future shortcodes

Here are a few additional shortcode ideas that would fit well in an IT-focused blog:

  • Diff viewer โ€” render configuration or code changes in a readable diff format

  • Badges โ€” display states such as Active, Disabled, or Deprecated with custom styles

  • Timelines โ€” visualize chronological steps, migrations, or project history

Summary

Hugo shortcodes allow you to::

๐Ÿ“„ Keeps Markdown clean and readable

๐Ÿ” Makes data reusable across multiple posts

๐Ÿงฉ Separates content from presentation

๐Ÿ›  Makes large among of data easy to maintain

โš ๏ธ Fails loudly if the file is missing

Well-designed shortcodes turn repetitive documentation patterns into reusable, expressive building blocks.

Saturday, January 17, 2026 Friday, January 16, 2026