Hugo is one of the most popular open-source static site generators. With its amazing speed and flexibility, Hugo makes building websites fun again. I started the task of moving the site you’re looking at from WordPress to Hugo back in March, and the more I work with Hugo, the more I like it.

One of the very convenient things about WordPress was that it made using images in my posts very easy. I just dumped an image in the WordPress' Gutenberg editor, and made sure it was resized to all necessary images size, and that everything was displayed correctly. In Hugo, there’s no such thing as the Gutenberg editor. There are some content management systems like Forestry.io and Netlify that you can put on top of Hugo, but one of the reasons I wanted to move to a static site generator was to avoid any sort of vendor lock-in.

No CMS or built-in editor means that Hugo users have to manage stuff like image resizing ourselves. But fear not. Like a lot of other things, Hugo makes this surprisingly easy.

Hooks and Processing

First off, let’s talk about Hugo’s Markdown render hooks. A render hook triggers whenever Goldmark - the Markdown parser used by Hugo - parses a particular piece of Markdown. As of Hugo v0.71.0, the hooks trigger on images, links, and headings. To resize images, we’ll create an image render hook.

The next thing we need to know about is Hugo’s image processing capabilities. As long as - and this is important - an image is available to Hugo as a page resource, i.e. part of a page bundle, Hugo can do all kinds of processing of the image. In this particular use-case, we’ll only resize the image, but it’s also possible to fit, fill, extract EXIF data, and apply various filters.

But enough talk, let’s get to work.

To create the image render hook, all you have to do is to create a file called render-image.html in layouts folder structure. The layout folder is probably hiding inside the theme you’re using.

layouts
└── _default
    └── _markup
        ├── render-image.html

Then we add the following to the render-image.html file:

{{ $image := (.Page.Resources.GetMatch .Destination).Resize "640x" }}
<img src="{{ $image.RelPermalink | safeURL }}" />

And that’s all you need, really. Hugo will now automatically resize all the images in your markdown to a 640 pixels wide image.

Beyond the Basics

The example above is rather basic, and we should make a few changes to make it a bit more intelligent. There is, for instance, no need to resize images that are already less than 640 pixels wide. This can be done with a some additional code.

{{ $image := .Page.Resources.GetMatch .Destination }}
{{ if gt $image.Width 640 }}
    {{ $image = $image.Resize "640x" }}
{{ end }}
<img src="{{ $image.RelPermalink | safeURL }}" />

The above change makes sure we only resize images that are wider than 640 pixels.

Next we want to make sure we render the alt and title attributes from the Markdown file as part of the generated HTML. This is done with a few simple changes to the last line.

{{ $image := .Page.Resources.GetMatch .Destination }}
{{ if gt $image.Width 640 }}
    {{ $image = $image.Resize "640x" }}
{{ end }}
<img src="{{ $image.RelPermalink | safeURL }}" {{ with .Text }} alt="{{ . }}" {{ end }} {{ with .Title }} title="{{ . }}" {{ end }}/>

Now both the alt and title attributes will be added if they exist in the Markdown file. Marvelous!

The final thing we want to add is a nice visual representation of the title in the form of a figcaption element. This also involves wrapping the img element in a figure element, but it’s still a very easy thing to do.

{{ $image := .Page.Resources.GetMatch .Destination }}
{{ if gt $image.Width 640 }}
    {{ $image = $image.Resize "640x" }}
{{ end }}
<figure>
    <img src="{{ $image.RelPermalink | safeURL }}" {{ with .Text }} alt="{{ . }}" {{ end }} {{ with .Title }} title="{{ . }}" {{ end }}/>
    {{ with .Title}} <figcaption>{{ . | markdownify }}</figcaption> {{ end }}
</figure>

And there you go! With only 8 lines of code, Hugo is now resizing all your Markdown images, and making sure everything is rendered as valid HTML.

Now go ahead and throw a couple of bucks at the lead Hugo developer for making this as easy as pie.