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.htmlContent 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:
Language is taken from the file extension
1{{< code-snippet example.txt>}}
Or you can declare the language
1{{< code-snippet example.txt ini>}}
- The result:
1[Test] 2Description="Some random text to demonstrate the `code-snippet` shortcode."
code do not put file with an .html extension, otherwise Hugo will try to build it.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 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 "name,description" >}}The result:
name description Element 1 A description of element 1. Element 2 A description of element 2.
NB: I have generated HTML output, so the right syntax to use it, is {{< >}} and not {{% %}} which is markdown-aware.
By then, I improved the table-snippet shortcode with:
✔ Uses positional parameters only
✔ Looks in the folder ./page/table first, then ./data
✔ Handles nested path rss/my-super-list
✔ Optional sorting arguments
✔ Optional filtering boolean arguments
The usage now:
{{< table-snippet DATA_FILE COLUMNS SORT FILTER >}}Usage Example:
{{< table-snippet rss/list "name,description,url" name active >}}
NB about Hugo:
- Page bundle (local to the
article/tableorarticle/tables) = Scoped to the page - Global data directory (
data/) = Reusable across the site - Hugo behavior is format-agnostic configuration, it matches all config files (
.yaml, .yml, .json, .tomlall work)
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, orDeprecatedwith custom stylesTimelines — visualize chronological steps, migrations, or project history
Summary
Hugo shortcodes act as a “controller”. It allows you to:
📄 Keeps Markdown clean and readable
🔁 Makes data reusable across multiple posts. (Loads data page or data/)
🧩 Separates content from presentation
🛠 Makes large among of data easy to maintain
⚠️ Handles errors, Fails loudly if the file is missing
Well-designed shortcodes turn repetitive documentation patterns into reusable, expressive building blocks.
By contrast, partials are used for reusable rendering logic:
- Receive normalized data
- Output HTML
- Apply formatting rules









