Hugo Tips, Shortcodes, and Fragments
Updated 2021-September-10

Page contents

News

2021-March-27  In this article, created fragment 10. get-assets-text, which is about a shortcode similar to the one discussed in fragment 9. get-leaf-text.

2021-February-1  As of today, this evolving⁠[1] article has been on the web for 1 year.🎂

 

Prerequisites

This article assumes you know the basics about the Hugo static site generator. It does not assume you know anything about Hugo shortcodes, which are introduced and discussed in fragments 4 through 13 below.

Commenting in website source files

Commenting is useful for writing notes to yourself and for holding fragments that you have used in the past or may use in the future. To learn about the comment syntax of many languages, see Infinite Ink’s Commenting Code Cheatsheet.

 

1. Commenting out executable code

In the Go Template language, which is also known as Go HTML, you can comment out⁠[2] executable code with the following single- or multi-line comment syntax.

{{/* Go HTML single-line comment */}}


{{/*

Go HTML
multi-line
comment

*/}}

The following also work.

{{- /* Go HTML single-line comment */ -}}


{{- /*

Go HTML
multi-line
comment

*/ -}}

Note that in this second style of Go Template comment syntax, a single space is required between each dash (-)[3] and its corresponding forward slash (/).

 

2. Commenting out notes and non-executable code

In a layout file or a Markdown content file, you can comment out text (such as an English-language note to yourself) or non-executable code with the following single- or multi-line comment syntax.

<!--  HTML or Markdown single-line comment -->

<!--

HTML or Markdown
multi-line
comment

-->
Do not use these HTML-style comments to comment out executable Go Template code in a Hugo layout file. If you do, Hugo will process the executable code and potentially generate error messages or change the layout logic.

 

3. Oddity about commenting in a block layout file

In a child block layout file, do not put a comment — or anything other than blank lines — outside a define-end container:

only blank lines here

{{ define "main" }}

Go Template code and comments here

{{ end }}

only blank lines here

 

One-off custom shortcodes

Hugo shortcodes are a way to use Go Template code within a content file. Reusable and one-off shortcodes usually live in the 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 the ones discussed in fragment 4 and fragment 5 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

 

4. “Hugo version” inline shortcode

Infinite Ink’s I Use This page displays this:

  • hugo v0.88.1+extended

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

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

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

 

The dashes[3] 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.

 

5. “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 am 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 Infinite Ink’s website-todo-done.adoc[4] content file.

{{< 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 highlighted 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⁠[5] 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 my 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.

6. 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 2021-09-10
           ----------
               👆
           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 Jan 2 15:04:05 2006 MST.

You can remember the reference date with the following mnemonic.

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

To learn about this, see:

 

7. 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.

 

8. years-since

{{- /*

USAGE:

  {{< years-since YYYY >}}

EXAMPLE:

  {{< years-since 2009 >}}

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 */ -}}
To avoid whitespace problems, I use the Go Template trim-whitespace delimiters {{-  and  -}} throughout the above years-since shortcode.

 

This 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 ~8.

 

️About variable names in Hugo

The names of Hugo variables (including those that are specified in config files and front matter)…

  • cannot contain a dash character (-),[3]

  • can contain underscores (_) and alphanumeric characters,

  • and are sometimes case sensitive.

Variable names that a Hugo user creates in a layout file — such as $thisyear and $thatyear — are called custom variables and must start with a dollar sign ($).

💡
Bep, Hugo’s lead developer, recommends all lower case snake_case — for example $this_year and $that_year — for custom variable names. For more about this, see discourse.gohugo.io’s Best practices for naming variables etc.

 

9. 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.GetMatch $filename -}}
{{- $file.Content -}}

 

Below are three examples.

Example 1: Get and highlight Python code

{{< highlight python "hl_lines=15" >}}
{{< 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 raw (unrendered) 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.

 

💡

You are less likely to have a problem if get-leaf-text’s argument (the file name) does not use one of the 13 file extensions that Hugo interprets as a known Markup language (.ad, .adoc, .asciidoc, .htm, .html, .markdown, .md, .mdown, .mmark, .pandoc, .pdc, .org, or .rst).

This is why I use the file extension .pdmd, which is meaningless to Hugo, rather than .pandoc or .pdc, which are meaningful to Hugo, in examples 2 and 3 above.

 

The highlight shortcode is discussed in fragment 13 below.

 

10. 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 get-assets-text EXAMPLE is used in Infinite Ink’s…

💡
To learn about the Hugo assets directory, which is where global resources are located, see gohugo.io/hugo-pipes/introduction/.

 

11. 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.GetMatch $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" >}}

 

HTML4 images vs HTML5 images

  • 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.

 

12. 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, .adoc, or .asciidoc) 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:

 

13. highlight

When I use Hugo’s built-in highlight shortcode and want to highlight 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 highlighted

{{< /highlight >}}

Or this:

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

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

{{< /highlight >}}

To learn about the highlight shortcode, see:

 

💡
To view a list of all possible highlighting languages, such as html 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 layout/shortcodes/ directory, for example to one of the following directories.

layout/shortcodes/
layout/shortcodes/3rdparty/
layout/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.

 

Escaping strings in content files from Hugo processing

14. Escaping emoji codes

In step 13.2.2. Include emoji codes in Infinite Ink’s Hugo Tutorial, I wrote about the following emoji codes.

:man_astronaut:
:star:
:woman_astronaut:
:bulb:
:wrench:
:sparkles:

Since I have enableEmoji: true in my config.yaml, Hugo emojifies the above strings in a content file to:

👨‍🚀
⭐
👩‍🚀
💡
🔧
✨

So how do you write about these strings without Hugo emojifying them? There is more than one way to escape emoji codes. The way I do it is to use an HTML entity for one of the colons in each emoji code. For example:

:man_astronaut&#58;
:star&#58;
:woman_astronaut&#58;
:bulb&#58;
:wrench&#58;
:sparkles&#58;

This trick works…

  • in some parts of a Markdown content file, but does not work in Markdown inline code or in a Markdown code block

  • throughout an AsciiDoc content file, but sometimes needs [subs="replacements"]

 

15. Escaping and commenting out Hugo shortcodes

In a Hugo content file, shortcodes are called using either the delimiters {{< and >}} or the delimiters {{% and %}}.

15.1. Escaping shortcodes

If you want to write about shortcodes, as I did in fragments 4, 5, 6, 7, 8, 9, 10, 11, 12, and 13 above, you need to escape these strings from being interpreted as being part of actual shortcode calls. Hugo uses the following syntax to escape these delimiters in a content source file:

{{</* something */>}}
   ^^           ^^
   Note         Note

{{%/* something */%}}
   ^^           ^^
   Note         Note

If you use the above delimiters in a content file, they will be rendered in the destination file as:

{{< something >}}

{{% something %}}

And these strings will not be interpreted as shortcode calls.

 

I learned about escaping Hugo shortcodes in the discourse.gohugo.io thread How is the Hugo Doc site showing shortcodes in code blocks?

 

️15.2. Commenting out shortcodes in Markdown

To comment out a shortcode in a Markdown (.markdown, .md, or .mdown) content source file, you can do this:

<!--
{ {< shortcode call >}}
-->

Notice the space between the two leading curly braces. Actually any character between the curly braces will denature the shortcode. For example, I like to use an exclamation mark because it is more noticeable than a space character:

<!--
{!{< shortcode call >}}
-->

 

️15.3. Commenting out shortcodes in AsciiDoc

To comment out a shortcode in an AsciiDoc (.ad, .adoc, or .asciidoc) content source file, you can do this:

////
{!{< shortcode call >}}
////

Or this:

// {!{< shortcode call >}}

 

💡
If you really want it to be noticeable that a shortcode is commented out, you could use !!! rather than ! to denature a shortcode call.

 

Hugo configuration

To learn about configuring Hugo, see gohugo.io/getting-started/configuration/.

16. frontmatter

I use the following YAML nested map, which is also known as a table,⁠[10] in my config.yaml because…

  1. I want date and publishDate to mean the same thing and

  2. if no date is specified, I want a reasonably sane date (:fileModTime) to be used rather than Hugo’s default date, which is 0001-Jaunary-01.

frontmatter:
  date: 
    - publishDate
    - :filename
    - date
    - :fileModTime
  publishDate: 
    - publishDate
    - :filename
    - date
    - :fileModTime
  lastmod: 
    - lastmod
    - :fileModTime

 

For more about this, see Stackoverflow.com’s Hugo Date vs PublishDate, which includes an answer by n m (i.e. me😄).

The TOML syntax for this table is in fragment 18 below.

 

17. markup

To override Hugo’s default markup settings, use a markup nested map in your project’s config file. Here is an excerpt of Infinite Ink’s config.yaml:

markup:
  goldmark:
    renderer:
      unsafe: true  # default is false
  highlight:
    style: tango    # default is monokai

 

Note that…

  • Without the unsafe: true setting, which is the highlighted line in this YAML fragment, raw HTML in a Goldmark-flavored Markdown source file will be replaced with <!-- raw HTML omitted --> in the markdownified⁠[11] destination file.

  • A nested-map markup setting like this does not work in the front matter of a content file. These variables must be set globally.

  • The colors and italics in this YAML fragment are thanks to tango-style syntax highlighting.

 

💡

To view examples of all possible values of markup.highlight.style, including monokai and tango, see the Chroma Style Gallery at xyproto.github.io/splash/docs/all.html.

 

For more about this, see the following.

 

The TOML syntax for this markup nested map, which is also known as a table, is at the bottom of the next fragment.

 

18. Dividing lines in a config.toml file

When I use a config.toml, I like to include comments like the three highlighted lines below.

### ---> Put tables below non-tables <--- ###


# next from “One-off shortcodes” above
enableInlineShortcodes = true


# next from fragment 13 above
enableEmoji = true 


### ---> Put TOML tables below here <--- ###


# next from fragment 15 above
[frontmatter]
  date = ["publishDate", ":filename", "date", ":fileModTime"]
  publishDate = ["publishDate", ":filename", "date", ":fileModTime"]
  lastmod = ["lastmod", ":fileModTime"]


# next from fragment 16 above
[markup]

  [markup.goldmark]
    [markup.goldmark.renderer]
      unsafe = true

  [markup.highlight]
    style = "tango"


### ---> Put tables below non-tables <--- ###

 

I use these triple-hash comments because I have spent a lot of time debugging Hugo websites when I, for example, put a non-table at the bottom of a config.toml.⁠😩

To learn about TOML, which is an alternative to YAML, see:

 

See also

Endnotes


1. Many Infinite Ink articles, including this one, are evergreen and regularly updated.
3. 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.
5. 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 two global functions, see the Hugo v0.53 release notes, especially the relevant GitHub commit and GitHub issues.
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.
10. A nested map is also known as an associative array, a dictionary, a dict, a hash table, a hash, a map, a mapping, a named map, an object, or a table. For more about this data-serialization object, see Infinite Ink’s YAML Includes Atoms, Maps, and Lists (Featuring the String ¯∖_(ツ)_/¯).
11. In Hugo, markdownify means convert Markdown to HTML.

Comments 👍 👎 📝

Please comment so I know I'm not speaking into the void. Also, your public comment might improve this page or help me to improve this page.