Wrap Image with Figure Tag on Hugo without Shortcode

Automatically wrap img HTML tag inside a figure and insert figcaption based on alt attribute

Recently I’ve been thinking of migrating my blog from Blogger.com to a static site. After doing some research I decided to use Hugo.

One thing I like about Hugo is that they can produce a site from Markdown files. Now this markdown file is following CommonMark spec by default, which is great. But I have a case to always wrap images of my content inside a figure tag.

I actually can do this using Hugo shortcode, but I don’t want to use that because I want to make sure my markdown files follow CommonMark spec. So it must be pure Markdown content with frontmatter, without filling it with a custom rule from Hugo.

The second choice is to use HTML directly in the markdown file. It’s a valid move because it’s allowable in CommonMark. However, writing an HTML tag is too verbose and not effective.

Luckily, I finally found a way to wrap markdown images automatically using the regex function from Hugo.

Objective

Based on the problem above, there is a way to turn a standard markdown image with alt text wrapped with figure tag and use its alt text as figcaption. Let’s see how we usually write an image in markdown.

There are two possibilities for producing images from markdown, with or without title text. Below are two examples of how you write an image in markdown.

![Alt Text](https://www.example.com/image.png)

![Alt Text](https://www.example.com/image.png "Title Text")

Using CommonMark spec, the markdown text above will turn into HTML content like the following code.

<p><img src="https://www.example.com/image.png" alt="Alt Text"></p>

<p><img src="https://www.example.com/image.png" alt="Alt Text" title="Title Text"></p>

Our objective is to wrap the image with a figure so it’ll look something like this.

<figure>
  <img src="https://www.example.com/image.png" alt="Alt Text">
  <figcaption>Alt Text</figcaption>
</figure>

<figure>
  <img src="https://www.example.com/image.png" alt="Alt Text" title="Title Text">
  <figcaption>Alt Text</figcaption>
</figure>

Solution (2018)

What you need to modify is the layouts/_default/single.html file. For example, let’s assume the content of the file is like the code listing below.

<article>

  {{ .Content }}
  
</article>

Now we just need to execute string replacement using regex. Hugo has a built-in function for that, it’s called replaceRE. So here is the code to wrap the image with a figure tag.

<article>

  {{ $reAltIn := "<p><img src=\"([^\"]+)\" alt=\"([^\"]+)\"*></p>" }}
  {{ $reAltOut := "<figure><img src=\"$1\" alt=\"$2\"><figcaption>$2</figcaption></figure>" }}
  {{ $altContent := .Content | replaceRE $reAltIn $reAltOut | safeHTML }}

  {{ $reAltTitleIn := "<p><img src=\"([^\"]+)\" alt=\"([^\"]+)\" title=\"([^\"]+)\"*></p>" }}
  {{ $reAltTitleOut := "<figure><img src=\"$1\" alt=\"$2\" title=\"$3\"><figcaption>$2</figcaption></figure>" }}
  {{ $finalContent := $altContent | replaceRE $reAltTitleIn $reAltTitleOut | safeHTML }}

  {{ $finalContent }}

</article>

And that’s it, your produced image HTML will be wrapped with a figure tag. Also, if you don’t set alt text, the image will stay the same wrapped with a p tag which is a good thing if the image isn’t meant to be used as a figure.

UPDATE 2024 Solution

Somehow recent version of Hugo changed the order of the generated HTML. The default output will look like the following snippet.

<p><img alt="Alt Text" src="https://www.example.com/image.png"></p>

<p><img alt="Alt Text" src="https://www.example.com/image.png" title="Title Text"></p>

Notice now that alt is the first attribute while previously src was the first one.

So we need to swap the solution and it’ll become something like the following code.

<article>

  {{ $reAltIn := "<p><img alt=\"([^\"]+)\" src=\"([^\"]+)\"*></p>" }}
  {{ $reAltOut := "<figure><img alt=\"$1\" src=\"$2\"><figcaption>$1</figcaption></figure>" }}
  {{ $altContent := .Content | replaceRE $reAltIn $reAltOut | safeHTML }}

  {{ $reAltTitleIn := "<p><img alt=\"([^\"]+)\" src=\"([^\"]+)\" title=\"([^\"]+)\"*></p>" }}
  {{ $reAltTitleOut := "<figure><img alt=\"$1\" src=\"$2\" title=\"$3\"><figcaption>$1</figcaption></figure>" }}
  {{ $finalContent := $altContent | replaceRE $reAltTitleIn $reAltTitleOut | safeHTML }}

  {{ $finalContent }}

</article>

It might change again in the future, so keep an eye on the new update!

Summary

Ok, that’s a little research I’ve done on the weekend to explore Hugo’s features. So far it’s been a fun experience tinkering with Hugo.

I’ll write more about Hugo next time.

References