Hugo imglink shortcode

In a recent post, I discussed moving my content into page bundles. In this post I take it a step further by introducing a shortcode to process images into a clickable thumbnail that links to the image.

When I started using Hugo, I lumped all the media files into the /static directory. This was the way one of the articles I read about Hugo did things so I thought it was “the way”. It quickly became annoying having to manage the files in one directory so I decided to organise them into folders that reflected the posts I’d created. For example, images for /content/posts/interesting-post.md would be stored in /static/interesting-post. This made management of the /static folder a bit easier but still caused me some issues not having images stored next to the post they belong too. It’s at this point that I discovered page bundles and was able to reorganise my post content as discussed in the previous post.

Now, one of the problems with storing images in /static is that there is no ability to process them for a post. This means that a multi-mb image will be downloaded in full unless a thumbnail is manually created, stored etc - another management headache. When I started to research page bundles I came across the Hugo docs for image processing, this is where Hugo can apply a bunch of changes to a source image and save them as a copy, for example all images get scaled down to 640 x 480.

In the modern era of web development, there is a lot of work put into responsive UIs and I’ve seen a number of solutions based on this. They use Hugo image manipulation to create a number of scaled images, then depending on the screen size an appropriately sized image will be available. I like this approach but what I really want to do is have a smallish thumbnail that is linked to the original image, this can then be clicked for more detail. After a bit of reading I found that this could be achieved with a shortcode, here’s how…

A shortcode is a way to embed a chunk of boilerplate into a Hugo page. There are several benefits to using shortcodes:

  • A shortcode is short - it saves repeating chunks of HTML in the post page, I’ll highlight this later;
  • Means HTML rendering doesn’t need to be “turned on”;
  • Abstracts complexity out of the post, this makes the raw post easier to read in a text editor - the whole point of Markdown;
  • Its reusable across the site; and
  • A change to the definition file is deployed across the whole site.

The imagelink shortcode I’ve created is used like this in a post:

...some post content...

{{< imglink title="Atari 2600 cartridges" src="pexels-kevin-bidwell-1373100.jpg" size="400x400" >}}

...some more post content

And this is how it renders:


Atari 2600 cartridges

Comparing that to the builtin way for including an image and I don’t think it’s much more complicated.

...some post content...

![Atari 2600 cartridges](pexels-kevin-bidwell-1373100.jpg)

...some more post content

Both take a title and a link to the file, my shortcode also take a size to scale too and that’s about it. If we wanted to replicate the same in HTML in a post, HTML would first need to be turned on, to do this requires an update to the config.toml file:

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

After this, a thumbnail would need to be created (tn_pexels-kevin-bidwell-1373100.jpg) and then the post updated to include the HTML that creates a linked thumbnail:

...some post content...

<a href="pexels-kevin-bidwell-1373100.jpg">
    <img src="tn_pexels-kevin-bidwell-1373100.jpg" alt="Atari 2600 cartridges">
    <figcaption>Atari 2600 cartridges</figcaption>
</a>

...some post content...

Now this isn’t a hugely complicated example but it does show that the HTML version of a link contains much more text. It also shows that there needs to be 2 copies of the image existing at the time of writing.

Reducing complexity

The code for the shortcode needs to be kept in a file which has the same name as the identifier for the shortcode, this file then needs to be saved in /layouts/shortcodes, for example the shortcode I created is imglink so the code for this resides in /layouts/shortcodes/imglink.html. Some of the themes will supply their own shortcodes, likewise there are some shortcodes built in to Hugo.

The content of the imglink.html file is as follows:

{{ $original := .Page.Resources.GetMatch (.Get "src") }} <!-- Get the first matching page resource with the filename given in src -->
{{ $title := .Get "title" }} <!-- Get the value passed in as title -->
{{ $size := .Get "size" }} <!-- Get the value passed in as size -->
{{ .Scratch.Set "image" ($original.Fit $size) }} <!-- Create an image which is a scaled version of the original image, keeping aspect ratio -->
{{ $image := .Scratch.Get "image" }} <!-- Get the new image and assign to $image -->

<!-- Create the HTML to use in the post, this populates the placeholders with the values determined above -->
<a href="{{ $original.RelPermalink }}">
    <img src="{{ $image.RelPermalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" alt="{{ $title }}">
</a>

Although this may look complicated on first viewing, it quickly becomes easy to understand. It’s obtaining the values passed into the shortcode, creating a resized copy of the original image and populating some boilerplate HTML which will then replace the shortcode in the posts at generation time. This reduces complexity in the post by keeping all this logic in a separate file and also reduces complexity of the build process as there are no extra steps needed to create thumbnails of the images.

Reuse

All shortcode and HTML examples are reusable across the post, but as the number of images in a post grow embedded HTML would become more complex to work with. Imagine if there was a mini gallery in the post with 20 images, with the shortcode that’s 20 images and 20 captions to manage, with HTML it’s 40 images, 20 alt texts and 20 captions to manage. As you can see here, the post can quickly become difficult to read compared to the shortcode and this only has 10 of the HTML links visible:


Comparing HTML and imagelink

Maintainable

As the shortcode is a reference to boilerplate that is substituted in the post at build time, it means that maintaining the shortcodes is very easy.

Imagine if we wanted to apply a site wide update of the images so that instead of being opened in the browser they are actually downloaded. With included HTML this would require a search for all the <a> tags within the post files, then each tag would need to be viewed in context to know if it needs to be updated to force download.

The update is a very simple one, we’re adding download to the <a> but the process of finding the tags to update is quite involved.

...some post content...

<a href="pexels-kevin-bidwell-1373100.jpg" download>
    <img src="tn_pexels-kevin-bidwell-1373100.jpg" alt="Atari 2600 cartridges">
    <figcaption>Atari 2600 cartridges</figcaption>
</a>

...some post content...

Contrast this with the change needed in the shortcode:

{{ $original := .Page.Resources.GetMatch (.Get "src") }} <!-- Get the first matching page resource with the filename given in src -->
{{ $title := .Get "title" }} <!-- Get the value passed in as title -->
{{ $size := .Get "size" }} <!-- Get the value passed in as size -->
{{ .Scratch.Set "image" ($original.Fit $size) }} <!-- Create an image which is a scaled version of the original image, keeping aspect ratio -->
{{ $image := .Scratch.Get "image" }} <!-- Get the new image and assign to $image -->

<!-- Create the HTML to use in the post, this populates the placeholders with the values determined above -->
<a href="{{ $original.RelPermalink }}" download>
    <img src="{{ $image.RelPermalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" alt="{{ $title }}">
</a>

The shortcode boilerplate has been updated with download but this has only had to be done once, there has also had to be zero requirement to review each <a> and understand it’s context. I know that the next time the site builds, all the images included using imglink will download the picture instead of opening it.

Hopefully this post has demonstrated the power of shortcodes and how they can benefit any Hugo based site. My personal opinion is that they are an acceptable compromise when compared with inserting raw HTML into posts. Now that my imglink shortcode has been set up, all the images in this site have been updated to use it.

Final note: Pexels has some great stock photography which can be used for free. The image of the Atari 2600 cartridges I chose was from Pexels, it’s by Kevin Bidwell and can be accessed here: https://www.pexels.com/photo/game-cartridges-1373100/