Hugo Shortcodes: How to Create and Use Them
Hugo shortcodes are reusable template fragments that can be embedded in Markdown content. They bridge the gap between Markdown’s intentional simplicity and the richer, more structured HTML that publishing sites often need — figure captions, callout boxes, video embeds, custom alert blocks, styled pull quotes — without requiring authors to write raw HTML in their content files.
Hugo ships with a small set of built-in shortcodes and provides a straightforward system for building your own.
Built-In Shortcodes
Hugo includes several shortcodes out of the box:
figure — renders an image with an optional caption, attribution, and link:
{{< figure src="/images/article-photo.jpg" alt="Description" caption="Photo by Jane Smith" >}}
youtube — embeds a YouTube video responsively by video ID:
{{< youtube dQw4w9WgXcQ >}}
vimeo — same pattern for Vimeo:
{{< vimeo 146022717 >}}
gist — embeds a GitHub Gist:
{{< gist username a1b2c3d4e5f6 >}}
highlight — syntax-highlighted code blocks with Chroma:
{{< highlight python "linenos=true,hl_lines=3 4" >}}
def hello():
return "world"
{{< /highlight >}}
Creating Custom Shortcodes
Custom shortcodes are HTML template files stored in layouts/shortcodes/. The filename becomes the shortcode name.
A Simple Callout Box
Create layouts/shortcodes/callout.html:
<div class="callout callout-{{ .Get "type" | default "info" }}">
<div class="callout-body">
{{ .Inner | markdownify }}
</div>
</div>
Use it in Markdown:
{{< callout type="warning" >}}
This feature is deprecated and will be removed in the next release.
{{< /callout >}}
The .Get "type" call retrieves the named parameter. .Inner captures the content between the opening and closing shortcode tags. markdownify renders any Markdown within the shortcode content.
Positional vs. Named Parameters
Shortcodes can use positional parameters (accessed by index) or named parameters (accessed by name):
<!-- Positional: {{< myshortcode "first" "second" >}} -->
{{ .Get 0 }} {{/* "first" */}}
{{ .Get 1 }} {{/* "second" */}}
<!-- Named: {{< myshortcode title="Hello" author="Jane" >}} -->
{{ .Get "title" }} {{/* "Hello" */}}
{{ .Get "author" }} {{/* "Jane" */}}
Named parameters are more readable in content files and should be preferred for shortcodes with more than one or two parameters.
A Pull Quote Shortcode
layouts/shortcodes/pullquote.html:
<blockquote class="pullquote">
<p>{{ .Inner | markdownify }}</p>
{{ with .Get "author" }}
<cite>— {{ . }}</cite>
{{ end }}
</blockquote>
Usage:
{{< pullquote author="Jane Smith, Editor" >}}
The best publications are the ones that treat their readers as intelligent adults.
{{< /pullquote >}}
A Responsive Video Embed
layouts/shortcodes/video.html:
<div class="video-wrapper">
<iframe
src="{{ .Get "src" }}"
title="{{ .Get "title" | default "Video" }}"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
</div>
Usage:
{{< video src="https://www.youtube.com/embed/VIDEO_ID" title="Interview with the Editor" >}}
An Alert/Notice Shortcode
layouts/shortcodes/notice.html:
{{ $type := .Get "type" | default "note" }}
<div class="notice notice-{{ $type }}">
<p class="notice-title">
{{ if eq $type "warning" }}⚠ Warning
{{ else if eq $type "tip" }}💡 Tip
{{ else if eq $type "important" }}📌 Important
{{ else }}📝 Note
{{ end }}
</p>
<div class="notice-content">
{{ .Inner | markdownify }}
</div>
</div>
Usage:
{{< notice type="tip" >}}
Hugo's `markdownify` function lets you write **Markdown** inside shortcode content.
{{< /notice >}}
Shortcodes with Page Context
Shortcodes have access to the page context through .Page, which allows shortcodes that interact with the current post’s data:
<!-- layouts/shortcodes/related.html -->
<div class="related-posts">
<h3>Related</h3>
{{ $related := .Page.Site.RegularPages.Related .Page | first 3 }}
{{ range $related }}
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
{{ end }}
</div>
Self-Closing vs. Paired Shortcodes
Shortcodes that take parameters but no inner content use self-closing syntax:
{{< figure src="/image.jpg" alt="Photo" >}}
Shortcodes that wrap content use paired syntax with an opening and closing tag:
{{< callout >}}
Content goes here.
{{< /callout >}}
In the shortcode template, check whether .Inner is expected. A self-closing shortcode called with paired syntax will not error, but .Inner will be empty. A paired shortcode called self-closing will also not error, but any .Inner rendering will produce nothing.
Markdown vs. HTML Shortcodes
The {{< >}} delimiter renders the shortcode output as HTML directly — Markdown in .Inner needs markdownify to render. The {{% %}} delimiter passes the shortcode output through the Markdown renderer first:
{{% notice %}}
This **Markdown** renders *automatically*.
{{% /notice %}}
Use {{% %}} when your shortcode’s inner content is always Markdown and you do not need the markdownify function in the template. Use {{< >}} with markdownify for more control, especially when the shortcode also outputs HTML structure around the content.
Organizing Shortcodes
For a publishing site with many shortcodes, group related templates and document parameters in code comments:
{{/*
callout.html
Parameters:
type (optional): "info" | "warning" | "tip" | "important" — defaults to "info"
Inner: Markdown content displayed in the callout body
Usage: {{< callout type="warning" >}} Content {{< /callout >}}
*/}}
Clear documentation in the shortcode file itself makes maintenance practical as the library grows.