Uses for Hugo inline shortcodes
Last updated: Sep 28, 2023
With inline shortcodes, write logic directly in a content page. Examples of making on-the-fly templates, variables, expandable acronyms, and counters.
A typical Hugo setup designates one directory for content (the text that people read) and another for presentation configuration (the logic that controls how the text is organized on the page). Such separation of concerns is quite sensible, but it adds some overhead to the writing process.
As it turns out, Hugo also supports inline shortcodes, defined directly in the content file. This means you can stay in your markdown file and still create shortcodes for templates, variables, and data mutations on the content within.
Inline shortcodes might be handy in a few cases.
- When the shortcode is scoped to one page
- This is the essential circumstance. If the shortcode is to be reused across content files, it should be shared in the
layouts/shortcodes
dir. - When the build and content repos are separated
- If the Hugo build system is modularized across different repos, an inline shortcode might be more convenient.
- For quick prototyping
- Sometimes, opportunities to template or reuse text are revealed only after writers start creating. With inline shortcodes, writers could prototype the shortcode in the markdown file, then abstract it out later.
- For fun
- It’s liberating to write shortcodes on the fly—no need to think about how things fit into the bigger picture.
This post documents ways to use inline shortcodes for common technical-writing problems.
All the shortcodes here also work as “normal” standalone functions.
So, again, if you want to reuse a shortcode here, remove the inline
suffix and add it to layouts/shortcodes
instead.
Requirements
To use inline shortcodes, you must:
Use Hugo version 0.52.0 or later
Enable inline shortcodes in your Hugo config:
enableInlineShortcodes: true
Syntax
To declare an inline shortcode:
Name your shortcode in opening and closing tags. Append
.inline
to the tag name.Within the shortcode tag, write the value that the shortcode will return.
<!--- opening tag --> {{< hello.inline >}} <!--- content --> Hello, you! <!--- closing tag --> {{< /hello.inline >}}
The first time you use the shortcode, the value renders. For example, the preceding snippet renders as follows:
Hello, you!
On subsequent calls, use one tag with a self-closing /
.
{{< hello.inline />}}
Like all shortcodes, inline shortcodes can use parameters, site or page data, and built-in Hugo functions.
Examples
These examples show inline shortcodes to solve some manual tasks that arise in my work as a technical writer.
Expand acronym names
This first example uses a variable to avoid typing verbose text.
The HTML abbr
element has a title
element that expands when the cursor hovers over it.
Here’s the example from the Mozilla Developer Network docs:
Ashok's joke made me
<abbr title="Laugh Out Loud">LOL</abbr>
big time.
That HTML renders as follows:
Ashok’s joke made me LOL big time.
It’s a chore to write that HTML each time, and the chance of a typo increases with each use. If you need to expand this acronym on only one page, you could use an inline shortcode to minimize typing.
The first time you use an acronym shortcode,
you type more,
{{< lol-abbr.inline >}}
<abbr title="laugh out loud">lol</abbr>
{{< /lol-abbr.inline >}}
On subsequent uses, you type less,
{{< lol-abbr.inline />}}.
The preceding snippet renders as follows:
The first time you use an acronym shortcode, you type more, lol
On subsequent uses, you type less, lol .
Use page frontmatter
For another way to make variables, you can add a new property to your frontmatter. The inline shortcode can access the value as a variable scoped to the page.
For example, you could use frontmatter to keep track of the last application version tested, and then use an inline shortcode to automatically update version references within the docs.
Add the version name in a frontmatter property:
last_version: Arctic effulgence
Reference the frontmatter value within your inline shortcode as
$.Page.Params.<key-name>
.{{% frontmatter.inline %}} This procedure was last tested on version `{{ $.Page.Params.last_version }}` {{% /frontmatter.inline %}}
This shortcode renders as follows:
This procedure was last tested on version
Arctic effulgence
The text processing could get much more complex if you want to mutate the data, or if your frontmatter includes arrays or nested object.
Make a page-specific template
You can also use inline shortcodes to template repetitive phrasing (or lists or tables) in a document.
A template could apply to even single sentences. For example, a page of conceptual content may end each section with links to learn more.
Write a snippet sentence with placeholder values.
This template uses variables for author names and their
id
within the Project Gutenberg online library. For clarity, I’m using named parameters likeauthor
instead of positional arguments.For more by {{ .Get "author" }}, visit the author's <a href="({{ $.Page.Params.library_url }}/{{ .Get "id" }})" Gutenberg page. </a>
Wrap the template in an inline shortcode tag, with values for the parameters.
{{< learn-more.inline author="Emily Dickinson" id="996" >}} For more by {{ .Get "author" }}, visit the author's <a href= "{{ $.Page.Params.library_url }}/{{ .Get "id" }}"> Gutenberg page. </a> {{< /learn-more.inline >}}
This shortcode generates the following HTML snippet.
For more by Emily Dickinson, visit the author’s Gutenberg page.
After declaring it once, I can reuse with new properties:
{{% learn-more.inline author="Sappho" id="32618" /%}}
{{% learn-more.inline author="shakespeare" id="65" /%}}
In the final rendering, these shortcodes appear as follows:
- For more by Sappho, visit the author's Gutenberg page.
- For more by Shakespeare, visit the author's Gutenberg page.
Make a counter
Sometimes, a tutorial has multiple sections and a required order. I generally avoid numbering these sections, because many things could go wrong: I could miscount, more steps could be added later, and so on.
An inline shortcode can serve as a counter to dynamically increment steps. For this one, I create two shortcodes:
The
counter-reset
starts or resets the counter,step
. It uses the.Scratch
function to store page data.{{% counter-reset.inline %}} {{ .Page.Scratch.Set "step" 1 }} {{% /counter-reset.inline %}}
The
increment
shortcode prints the value of the counter and then increments it.{{% increment.inline %}} {{ .Page.Scratch.Get "step" */}} {{ .Page.Scratch.Add "step" 1 */}} {{% /increment.inline %}} <!--- call it again --> {{% increment.inline %}}
For demonstration purposes, I’ll incoporate the counter with a template, wrapping the increment
function around text to describe a procedure.
{{% counter-reset.inline %}}
{{- .Page.Scratch.Set "step" 1 -}}
{{% /counter-reset.inline %}}
# How to run a race
{{% increment.inline "start at the beginning" /*%}}
## Step {{ .Page.Scratch.Get "step" }}: {{ .Get 0 }}
{{- .Page.Scratch.Add "step" 1 -}}
{{%/* /increment.inline %}}
{{% increment.inline "run to the middle" /%}}
{{% increment.inline "finish strong" /%}}
# How to technically write
<!--- reset counter -->
{{% counter-reset.inline /%}}
{{% increment.inline "say what you're going to say" /%}}
{{% increment.inline "say it" /%}}
{{% increment.inline "say what you said" /%}}
# How to run a race
## Step 1: start at the beginning
## Step 2: run to the middle
## Step 3: finish strong
# How to technically write
<!--- reset counter -->
## Step 1: say what you're going to say
## Step 2: say it
## Step 3: say what you said
Synthesis: First the full phrase, then abbreviate with tooltip
This example uses one shortcode to write the entire phrase the first time a term is used, then to write the abbreviation with a tooltip all other uses. This way, you can move acroymns around and always be sure that they are defined the first time.
This combines techniques from previous sections.
- The page frontmatter defines a word and its acronym.
- The shortcode uses the
Scratch
pad to determine whether the acroymn has been used.
This page has the following frontmatter:
lol:
term: Laugh out loud
acronym: LOL
The following shortcode uses with
to check whether the shortcode has already been used, then use that as a condition to print different strings.
The first time, I'll write an inline shortcode
and expand the entire phrase.
And that phrase is...
<!-- define acronym -->
- {{< acro.inline >}}
<!-- use with to if check word exists -->
{{ with .Page.Scratch.Get "lol_count" }}
<!-- If it exists, write abbr -->
<abbr title="{{ $.Page.Params.lol.term }}">
{{ $.Page.Params.lol.acronym }}</abbr>
<!-- If not, write whole term, then indicate it was used by setting counter -->
{{ else }}
{{ $.Page.Params.lol.term }} ({{ $.Page.Params.lol.acronym }})
<!-- change state of variable to indicate use -->
{{ .Page.Scratch.Set "lol_count" 1 }}
{{ end }}
{{< /acro.inline >}}
Now I'll use it a few more times:
- 2nd
{{< acro.inline />}}
- 3rd
{{< acro.inline />}}
- 4th
{{< acro.inline />}}
Here is how it renders:
The first time, I’ll write an inline shortcode and expand the entire phrase. And that phrase is…
- laugh out loud (LOL)
Now I’ll use it a few more times.
- 2nd LOL
- 3rd LOL
- 4th LOL
Discussion
As I wrote this post, I learned much more about shortcodes in general than I did about inline shortcodes.
Inline shortcodes are fun to write, but they can turn a page into a soupy, tangled mess (for example, the source for this page has started to get confusing to navigate).
Let me reiterate one more time: if you want to reuse a shortcode on another page, add it to your layouts
.
Besides that, some other drawbacks exist:
- They make the document more complicated to understand.
- They have some technical limitations. For example, you can’t nest them.
- They return values when declared, so you can’t write your functions in one part of the page and use them in another.
That said, I think inline shortcodes can be pretty handy, and I’ll probably start using them more. I especially see using them to template repetitive pages and to prototype.
Read more
- Hugo issue #4011. This issue is where the inline shortcodes were proposed. Aside from historical interest, it has a good discussion about naming and design decisions.
- Hugo Shortcode docs. Canonical docs about all shortcodes, including inline ones.
- Hugo functions. All these are available in your inline shortcodes.