Deprecating shortcodes with render hooks

When I moved this blog to Hugo, one of the features that most impressed me was shortcodes. These are little snippets that can be used to add complex formatting and content to posts without having to use raw HTML. For example, instead of pasting in the embed code for a Xweet or YouTube video, you use a shortcode like this:

{{< youtube VIDEO-ID >}}
{{< tweet USERNAME ID >}}

You can write custom shortcodes in the Go HTML template language Hugo uses, and they can become quite elaborate. It’s a great way to reuse complex, non-standard HTML formatting, though I’ve sometimes found the syntax a bit cumbersome.

Recently, however, Hugo has been adding render hooks, which allow site developers to override the default Markdown-to-HTML conversion process for several elements. It started with headings, links and images, which allowed me to replace my captioned image shortcode with plain Markdown. As a result, this shortcode:

{{< figure src="/path/to/image" caption="Image caption" >}}

Has now been replaced with this Markdown, which produces the same HTML:

![Image caption](/path/to/image)

More recently, Hugo added blockquote render hooks.1 The blockquote is not a particularly complex element. Per the Hugo docs, default behaviour is to turn this:

> Some text

Into this:

<blockquote>Some text</blockquote>

However, between the use of attributes and the devs’ inclusion of GitHub/Obsidian-style alert syntax, this one feature has allowed me to deprecate multiple shortcodes, while also providing enhanced formatting possibilities (hitherto unrealised).

First, I replaced the info box shortcode, which looked like this:

{{% info-box %}}
**Did you know?**

An interesting tangent.
{{% /info-box %}}

With Markdown like this:

> [!note] Did you know?
> An interesting tangent.

The first line of the alert blockquote includes a type (in square brackets) and a title (the rest of the line). Within the blockquote template, you can do anything with this information – display the title with special formatting, apply different formatting to different alert types, etc… This is much more flexibility than my old shortcode provided, for less typing.

The render hook also provides an optional sign argument, which can be + or -. In Obsidian, this is used to make the block foldable, like the <details> element in HTML. I use this element occasionally on this blog, usually for hiding spoilers. Previously, I had a shortcode for it:

{{% accordion "Click to reveal spoilers" %}}
Soylent Green is made of people.
{{% /accordion %}}

But now I’ve been able to fold that formatting into my blockquote template, allowing for this syntax:

> [!spoilers]+ Click to reveal spoilers
> Soylent Green is made of people.
Click to reveal spoilers

And for regular old blockquotes, I can use attributes to ensure consistently styled citations across the blog.

> 85% of quotes on the internet are made up.
{author = "Abraham Lincoln" source = "Gettysburg Address"}

85% of quotes on the internet are made up.

Abraham Lincoln, Gettysburg Address

And lastly,

Unadorned blockquotes still work fine.

John Gruber’s original vision for Markdown was for it to be readable in both rendered and unrendered forms. Much of the inspiration for its syntax came from existing conventions for decorating plain text – emphasising words with surrounding *s, underlining headings with rows of =/-, quoting with >, etc. To this day, Hacker News commenters include links using Markdown reference-style syntax which is never rendered.

In the twenty years since its first release, Markdown’s syntax has been extended by numerous companies and individuals. Goldmark, the Markdown parser used by Hugo, has built-in support for strikethroughs, tables, footnotes, definition lists2 and checklists, among other things. To my eye, most of the syntax for these extensions adheres to the original spirit of readability – at the very least, none of it is more arcane than the image syntax in the original spec. The definition list syntax doesn’t really look like anything, but I don’t have a better alternative. When it comes to prose markup, I’ve decided that a couple of cryptic glyphs are preferable to the verbose syntax of Hugo shortcodes and plain HTML.3 Implicit is sometimes better than explicit.

Attributes, like the ones I’ve used for my quote above, are where we cross over from marking up text for human eyes to marking it up for the computer. Attributes aren’t strictly necessary for this very simple use-case – I could just write the citation out manually, but I’m sacrificing a little readability for consistency. In theory, I could avoid this by including some more complex logic in the blockquote template, but that would be a very hacky way to extend Markdown syntax. In other words, a project for another day.


  1. Also table hooks. Still waiting for footnote hooks↩︎

  2. I was informed that this existed shortly after implementing a shortcode for it. So score another one for Markdown over shortcodes. ↩︎

  3. Remember, Markdown is a superset of HTML↩︎


similar posts
webmentions(?)