Hugo Shortcodes:
How to Include Go Templates in Hugo Content Files

Updated  2023-April-7

Page contents

News

Ongoing  According to Repology, the newest packaged Hugo is version newest packaged version of Hugo. To keep up with Hugo releases, see github.com/gohugoio/hugo/releases, discourse.gohugo.io/c/announcements, or twitter.com/@GoHugoIO.

2022-December-23  As of today, this evolving⁠[1] article has been on the web for 1 year.🎂 Before today, most of this article was part of Infinite Ink’s Hugo Tips and Fragments.

 

Prerequisite

This article assumes you know the basics about the Hugo static site generator. To learn about Hugo, see Infinite Ink’s #gohugo Portal.

 

What is a Hugo shortcode

A Hugo shortcode is a chunk of Go Template code⁠[2] that you can use within a Hugo content file. To learn about Hugo shortcodes, see…

 

One-off custom shortcodes

Reusable and one-off Hugo shortcodes usually live in a Hugo project’s layouts/shortcodes/ directory but, starting with Hugo v0.52, you have the option to put a shortcode directly into a content file by using an “inline shortcode.” This is especially useful for one-⁠off shortcodes such as shortcode 1 and shortcode 2 below.

To use inline shortcodes you need to set enableInlineShortcodes to true in your Hugo config file by putting this in your config.yaml:

enableInlineShortcodes: true

Or this in your config.toml:

enableInlineShortcodes = true

 

 

1. “Hugo version” inline shortcode

Infinite Ink’s I Use This (www.ii.com/uses/) article displays this:

  • hugo v0.111.3+extended

Each time Hugo builds the Infinite Ink website, the part after the string “hugo ” is generated by the following fragment in the article’s source file (uses.adoc[3]).

v{{< hversion.inline >}}
  {{- hugo.Version -}}
{{< /hversion.inline >}}+extended

To learn about the contextless⁠[4] hugo function, including hugo.Version (used just above here) and hugo.IsProduction (used in the next section), see gohugo.io/functions/hugo/.

 

The dashes[5] in the delimiters {{-  and  -}} above tell hugo to trim whitespace around the output of hugo.Version. These trim-whitespace delimiters must be specified exactly like this, i.e.:

  • a trailing space in the left delimiter,

  • a leading space in the right delimiter,

  • and no whitespace between either curly bracket and its dash.

 

2. “Writing In Progress” inline shortcode

On Infinite Ink’s To-⁠Do, Doing, and Done Lists⁠🚧, in the section Writing In Progress & Experiments, there is a list of articles that have not yet been published. For production — i.e. what you see — this is a list of titles without links, but for me, when I’m developing the Infinite Ink website with the hugo server command, this is a list of titles with links to these unpublished articles. I do this by putting my writing-in-progress articles in a directory named content/wip/[6][7] and by putting the following inline shortcode in the article’s source file, which is website-todo-done.adoc.⁠[3]

{{< wip.inline >}}
<ul>
{{ range (where site.RegularPages.ByTitle "Section" "eq" "wip") }}
  <li>
  {{ if hugo.IsProduction }}
      <em>{{ .Title }}</em>
  {{ else }}  <!-- not production -->
      <a href="{{ .RelPermalink }}"><em>{{ .Title }}</em></a>
  {{ end }}  <!-- end if-else -->
  </li>
{{ end }}  <!-- end range -->
</ul>
{{< /wip.inline >}}

 

💡

In the emphasized line above:

  • The parentheses surrounding the where clause are not needed, but I like to use them anyway because it reminds me that the syntax of the Go HTML range function is range COLLECTION.

  • In this context, site.RegularPages is equivalent to .Site.RegularPages. I prefer to use site.RegularPages because it is a more general way to collect a project’s regular pages. To learn about Hugo’s contextless⁠[4] global site function, see gohugo.io/variables/site/#get-the-site-object-from-a-partial.

  • The "eq" in the phrase "Section" "eq" "wip" is not needed because it is the default operator in a where clause. I like to specify it anyway as a note to self about the general syntax of the Go HTML where function.

 

I do things to ensure that no WIP[6] article is mentioned anywhere on Infinite Ink other than on Infinite Ink’s To-⁠Do, Doing, and Done Lists⁠🚧. For example, I specify appropriate build options.

 

Reusable custom shortcodes

The shortcodes in this section reside in Infinite Ink’s layouts/shortcodes/ directory and can be called from any content file by using the syntax specified in the USAGE comment at the top of the shortcode.

3. build-date

{{/*

USAGE:

  {{< build-date >}}

Path: layouts/shortcodes/build-date.html
Purpose: display current date

*/}}

{{- now.Format "2006-01-02" -}}

Here it is in action:

page built 2023-04-11
           ----------
               👆
           generated by build-date shortcode

 

About dates in Hugo📆

The string 2006-01-02 in the above build-date shortcode specifies the date format. Note that 2006, 01, and 02 are not a random year, month, and day. Instead they come from the Go reference date, which is Mon Jan 2 15:04:05 2006 MST.

You can remember the reference date with the following mnemonic.

Mon Jan 2 15:04:05 2006 MST
      1 2  3  4  5    6  -7

To learn about this, see:

 

4. omnicomment

{{/*

USAGE:

    {{< omnicomment >}}
        commented
        out
    {{< /omnicomment >}}

Forked from: github.com/gohugoio/hugoDocs/blob/master/layouts/shortcodes/todo.html

Path: layouts/shortcodes/omnicomment.html

Purpose: comment in content file written in any markup language Hugo supports

Why 1: no comment translation needed when change file from, e.g., .md to .adoc

Why 2: comment out big chunk that contains multiple markup comments[8]

*/}}

{{ if .Inner }}{{ end }}

Note that this is a no-op shortcode.

 

5. years-since

Both of the below versions of the years-⁠since shortcode work. When I first wrote this shortcode in 2020…

  • I did not know that bep (Hugo’s lead developer) recommends all lower case snake_case🐍 for user-⁠defined variable names

  • and I did not know about now.Year, which simplifies things.

 

2023 version of years-since

{{- /*

USAGE:

  {{< years-since YYYY >}}

EXAMPLE:

  {{< years-since 2013 >}}

Path: layouts/shortcodes/years-since.html

Why: as time goes on, this will still be the approximate number of years since YYYY

*/ -}}

{{- $this_year := now.Year -}}
{{- $that_year := .Get 0 -}}
{{- sub $this_year $that_year -}}

{{- /* clear trailing whitespace */ -}}

 

2020 version of years-since

{{- /*

USAGE:

  {{< years-since YYYY >}}

EXAMPLE:

  {{< years-since 2013 >}}

Path: layouts/shortcodes/years-since.html

Why: as time goes on, this will still be the approximate number of years since YYYY

*/ -}}

{{- $thisyear := int (now.Format "2006") -}}
{{- $thatyear := .Get 0 -}}
{{- sub $thisyear $thatyear -}}

{{- /* clear trailing whitespace */ -}}

 

Example usage

The years-since shortcode is used in the following sections of Infinite Ink articles.

  • Endnotes section of Commenting Code Cheatsheet,

  • What is Markdown? section of Ordinary and Extraordinary Markdown,

  • and right here to state that the number of years Hugo has existed is ~10.

 

Shortcode tips

️Trimming whitespace

 

️Variable names in Hugo

To learn about Hugo variable names, such as $this_year and $that_year, see Infinite Ink’s Variable and Parameter Names in Hugo (featuring camelCase⁠🐫 and snake_case⁠🐍).

 

6. get-leaf-text

{{- /*

USAGE:

  {{< get-leaf-text "filename" >}}

EXAMPLE:

  {{< get-leaf-text "config.py" >}}

Path: layouts/shortcodes/get-leaf-text.html
Purpose: get content of text file located in current leaf bundle

*/ -}}

{{- $filename := .Get 0 -}}
{{- $file := .Page.Resources.Get $filename -}}
{{- $file.Content -}}

 

Below are three examples.

Example 1: Get and highlight Python code

{{< highlight python "hl_lines=15,linenos=inline,anchorlinenos=true" >}}
{{< get-leaf-text "config.py" >}}
{{< /highlight >}}

The above example is used in the source of Infinite Ink’s qutebrowser’s Template config.py.

 

Example 2: Get and render Pandoc Markdown

{{% get-leaf-text "mainmatter-get.pdmd" %}}
 👆                                    👆
 Note                                  Note

 

Example 3. Get and highlight unrendered (raw) Pandoc Markdown

{{< highlight markdown >}}
{{< get-leaf-text "mainmatter-get.pdmd" >}}
{{< /highlight >}}

 

Examples 2 and 3 are both used in the source of Infinite Ink’s Links and Footnotes in Markdown. Note that Example 2 uses the {{% and %}} shortcode delimiters and Example 3 uses the {{< and >}} shortcode delimiters.

 

💡
  • Example 1 and example 3 are each an example of using nested shortcodes.⁠🪆

 

7. get-assets-text

{{- /*

USAGE:

  {{< get-assets-text "relative/path/to/filename" >}}

EXAMPLE:

  {{< get-assets-text "qb/config-fragment1.py" >}}

Path: layouts/shortcodes/get-assets-text.html
Purpose: get content of text file located below assets/text/

*/ -}}

{{- $file := resources.Get (printf "text/%s" ($.Get 0)) -}}
{{- $file.Content -}}

 

The above EXAMPLE get-assets-text is used in Infinite Ink’s…

💡

 

8. html4-get-bundle-image🖼️

{{/*

USAGE:

{{< html4-get-bundle-image "filename" "alt text" "width" "link" >}}

NOTE: parameters 1 & 2 required, parameters 3 & 4 optional

EXAMPLE 1:
{{< html4-get-bundle-image "gologo.png" "Go Logo" >}}

EXAMPLE 2:
{{< html4-get-bundle-image "gologo.png" "Go Logo" "50%" >}}

EXAMPLE 3:
{{< html4-get-bundle-image "gologo.png" "Go Logo" "25%" "https://blog.golang.org/go-brand" >}}

PATH: layouts/shortcodes/html4-get-bundle-image.html

PURPOSE: get image located in current bundle

*/}}


{{ $filename := .Get 0 }}
{{ $alt := .Get 1 }}
{{ $width := .Get 2 }}
{{ $link := .Get 3 }}

{{ $file := .Page.Resources.Get $filename }}


{{ with $link }}
<a href="{{ . }}">
{{ end }}
<img
  src="{{ $file.RelPermalink }}"
  alt="{{ $alt }}"
  {{ with $width }} width="{{ . }}" {{ end }}
>
{{ if $link }}
</a>
{{ end }}

 

Below are three examples.

Example 1: Unspecified width

Shortcode call
{{< html4-get-bundle-image "gologo.png" "Go Logo" >}}
Go Logo

Example 2: 50% width

Shortcode call
{{< html4-get-bundle-image "gologo.png" "Go Logo" "50%" >}}
Go Logo

Example 3: 25% width and linked

Shortcode call
{{< html4-get-bundle-image "gologo.png" "Go Logo" "25%" "https://blog.golang.org/go-brand" >}}
Go Logo

 

HTML4 img tag vs HTML5 img tag

  • In both HTML4 and HTML5, it is valid to use alt="" as an img attribute, but you should do this only if an image is for decoration and is not important to the meaning of a web page. For example, the images on Infinite Ink’s #gohugo Portal and #golang Portal are specified with alt="" because they are just for decoration.

 

Bundle image alternative: Use Markdown image syntax

If your leaf bundle looks something like this…

.
└── hugo-tips-fragments/
    ├── index.md
    └── gologo_fuchsia.png

then you can use this Markdown image syntax in index.md to reference the image:

![Alt text](gologo_fuchsia.png)
            ------------------
                    👆
            path relative to index.md

which renders as:

Alt text

 

9. embed-twitter-list

{{/*

USAGE:

  {{% embed-twitter-list listname %}}

EXAMPLE:

  {{% embed-twitter-list zeitgeist %}}

Path: layouts/shortcodes/embed-twitter-list.adoc
Purpose: Embed heading and Twitter list in a page written in AsciiDoc

*/}}


{{ $listbasename := .Get 0 }}

=== @nm/lists/{{- $listbasename }}

++++
<a class="twitter-timeline" href="https://twitter.com/nm/lists/{{- $listbasename -}}?ref_src=twsrc%5Etfw">A Twitter List by @nm <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
++++

This shortcode is used in some Infinite Ink portals, for example in the #zeitgeist Portal.

This shortcode uses AsciiDoc syntax and is meant to be used in an AsciiDoc (.ad or .adoc) content source file and called with the {{% and %}} shortcode delimiters (not the {{< and >}} shortcode delimiters).

 

Built-in shortcodes

Hugo includes built-in shortcodes, which you can read about at:

A good way to learn about writing your own shortcodes is to look at the source code of Hugo’s built-in shortcodes. That code is available on GitHub at:

 

10. highlight

When I use Hugo’s built-in highlight shortcode and want to emphasize lines, I almost always need to look up the syntax, which can look like this:

{{< highlight html "hl_lines=3-5" >}}

html code that will be displayed with lines 3, 4, and 5 emphasized

{{< /highlight >}}

Or this:

{{< highlight toml "hl_lines=1 12 33" >}}

toml code that will be displayed with lines 1, 12, and 33 emphasized

{{< /highlight >}}

To learn about the built-in highlight shortcode, see:

 

💡
To view a list of all possible highlighting languages, such as html, python, and toml, see gohugo.io/content-management/syntax-highlighting#list-of-chroma-highlighting-languages.

 

Hugo docs shortcodes

More shortcodes, including admonition shortcodes, are available in the Hugo Documentation repository in this directory:

To use one of the Hugo docs shortcodes, copy it to somewhere below your project’s layouts/shortcodes/ directory, for example to one of the following directories.

layouts/shortcodes/
layouts/shortcodes/3rdparty/
layouts/shortcodes/hugodocs/
💡

The Hugo docs note, tip, and warning admonition shortcodes start with the following line.

{{ $_hugo_config := `{ "version": 1 }` }}

This tells Hugo to process this shortcode the way that Hugo v0.54.0, and earlier do. To learn about this, see the Hugo v0.55.0 release notes,

 

References

See also

Endnotes


1. Many Infinite Ink articles, including this one, are evergreen and regularly updated.
2. Go Template code is also known as Go HTML. For details, see gohugo.io/categories/templates/, golang.org/pkg/html/template/, and golang.org/pkg/text/template/.
4. You can tell that the Hugo functions hugo and site are contextless by noticing that they do not have a leading dot (.). To learn more about these global functions, see the Hugo v0.53 release notes, especially the relevant GitHub commit and GitHub issues.
5. The Unicode name for the “dash” character (-) is “hyphen-minus,” but its proper name is rarely used. Instead, it is usually called “dash,” “hyphen,” or “minus.” Details are at wikipedia.org’s Hyphen-minus.
6. WIP can mean “work in progress” or “writing in progress.”
7. The front matter of some of Infinite Ink’s WIP articles include draft: true. Only the titles of the WIP articles that are not drafts are listed when I publish Infinite Ink’s To-⁠Do, Doing, and Done Lists⁠🚧 page.
8. By “multiple markup comments,” I mean markup comments that are not omnicomments. This gets around the problem of trying to nest markup comments, which does not work (because the first close-comment markup tag will end the outermost open-comment markup tag).
9. Another example of a shortcode and function having the same name: the relref shortcode and relref function.

Discuss or share 📝 🤔 🐘