Overview
Hugo provides content authoring features beyond standard markdown: shortcodes for reusable content components, render hooks to customize how markdown elements are processed, taxonomies for content classification, and page bundles for co-located assets. These features work independently of theme choice.
Built-in Shortcodes
Hugo includes several shortcodes:
<!-- Syntax-highlighted code with line numbers -->
{{</* highlight go "linenos=true,hl_lines=3" */>}}
func main() {
fmt.Println("Hello")
fmt.Println("Highlighted line")
}
{{</* /highlight */>}}
<!-- Figure with caption -->
{{</* figure src="/images/photo.jpg" title="Caption" alt="Description" */>}}
<!-- Cross-reference another page -->
[Link text]({{</* ref "other-page.md" */>}})
<!-- Relative reference -->
[Related]({{</* relref "sibling-page.md" */>}})
<!-- GitHub Gist -->
{{</* gist username gist-id */>}}
Creating Custom Shortcodes
Shortcodes live in layouts/shortcodes/. The filename becomes the shortcode name.
Simple Shortcode with Parameters
layouts/shortcodes/callout.html:
<div class="callout callout-{{ .Get "type" | default "info" }}">
<strong>{{ .Get "title" | default "" }}</strong>
{{ .Inner | markdownify }}
</div>
Usage in content:
{{</* callout type="warning" title="Important" */>}}
This is a warning message with **markdown** support.
{{</* /callout */>}}
Positional Parameters
layouts/shortcodes/badge.html:
<span class="badge badge-{{ .Get 0 }}">{{ .Get 1 }}</span>
Usage: {{</* badge "success" "Stable" */>}}
Shortcode Accessing Page Variables
layouts/shortcodes/last-modified.html:
<time datetime="{{ .Page.Lastmod.Format "2006-01-02" }}">
Last updated: {{ .Page.Lastmod.Format "January 2, 2006" }}
</time>
Common Shortcode Patterns
Tabs component (layouts/shortcodes/tabs.html and layouts/shortcodes/tab.html):
<!-- layouts/shortcodes/tabs.html -->
<div class="tabs">
<div class="tab-buttons">
{{ range $i, $e := .Scratch.Get "tabs" }}
<button class="tab-btn{{ if eq $i 0 }} active{{ end }}" data-tab="{{ $i }}">{{ .name }}</button>
{{ end }}
</div>
<div class="tab-panels">
{{ .Inner }}
</div>
</div>
Code block with filename (layouts/shortcodes/file.html):
<div class="code-file">
<div class="code-filename">{{ .Get "name" }}</div>
{{ .Inner | markdownify }}
</div>
Usage:
{{</* file name="config.yaml" */>}}
` ``yaml
key: value
` ``
{{</* /file */>}}
Render Hooks
Render hooks customize how Hugo processes standard markdown elements. Place them in layouts/_markup/.
Custom Link Rendering
layouts/_markup/render-link.html:
<a href="{{ .Destination | safeURL }}"
{{- with .Title }} title="{{ . }}"{{ end }}
{{- if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener"{{ end }}>
{{- .Text | safeHTML -}}
</a>
This makes external links open in a new tab automatically.
Custom Image Rendering
layouts/_markup/render-image.html:
<figure>
<img src="{{ .Destination | safeURL }}" alt="{{ .Text }}"
{{- with .Title }} title="{{ . }}"{{ end }} loading="lazy">
{{- with .Title }}
<figcaption>{{ . }}</figcaption>
{{- end }}
</figure>
Custom Heading Rendering
layouts/_markup/render-heading.html:
<h{{ .Level }} id="{{ .Anchor }}">
{{ .Text | safeHTML }}
<a class="heading-anchor" href="#{{ .Anchor }}">#</a>
</h{{ .Level }}>
Custom Code Block Rendering
layouts/_markup/render-codeblock.html:
{{ $lang := .Type }}
<div class="code-block" data-lang="{{ $lang }}">
<div class="code-header">
<span class="code-lang">{{ $lang }}</span>
<button class="copy-btn">Copy</button>
</div>
{{ .Inner | highlight $lang "" }}
</div>
Taxonomies
Hugo supports tags and categories by default. Add custom taxonomies in hugo.toml:
[taxonomies] tag = 'tags' category = 'categories' author = 'authors' technology = 'technologies'
Use in front matter:
--- title: "My Post" tags: ["go", "hugo"] categories: ["tutorials"] authors: ["bill"] technologies: ["hugo", "github-actions"] ---
Hugo auto-generates list pages at /tags/, /categories/, /authors/, /technologies/.
Page Bundles and Resources
Leaf bundles co-locate a page with its assets:
content/blog/my-post/ ├── index.md # Page content ├── hero.jpg # Page resource ├── diagram.svg # Page resource └── data.csv # Page resource
Access resources in templates:
{{ with .Resources.GetMatch "hero.jpg" }}
<img src="{{ .RelPermalink }}" alt="Hero">
{{ end }}
{{ range .Resources.Match "*.svg" }}
<img src="{{ .RelPermalink }}" alt="{{ .Title }}">
{{ end }}
Table of Contents
Hugo auto-generates a table of contents from headings:
<!-- In a template -->
{{ .TableOfContents }}
Configure depth in hugo.toml:
[markup]
[markup.tableOfContents]
startLevel = 2
endLevel = 4