Lazily Loading Resized Images on a Hugo Photoblog

I rebuilt A Strange Kind of Madness using Hugo a month or so ago. As with most photoblogs, it has pages with many images on them, and I was inspired by Photo Stream to load these images lazily.

Image resizing

If you want Hugo to resize images when it builds a site, you need to place your images alongside posts, so they are considered page resources. So, I put each post in a folder with its associated image and reference it in a field called, shockingly, image in the post front matter.

$ ls content/posts/2020-03-31-my-post
20200331-4491.jpg
index.md

$ cat content/posts/2020-03-31-my-post/index.md
+++
title = My Post
date = 2020-03-31T10:42:00+08:00
image = 20200331-4491.jpg
+++

Adding Lazyload

Roll-up pages have many thumbnails and benefit most from lazy loading.

First up, add the lazyload javascript library to your site build.

import Lazyload from "lazyload";

// Fire up our lazyloading (just initialising it does the job)
const _lazyload = new Lazyload();

The library’s default configuration targets images with the lazyload class and loads the image stored in the data-src attribute. If you place an image on the standard src attribute, it will be treated as a placeholder.

Placeholder images

I wanted something more interesting than a sea of grey rectangles for placeholder images. I had a look at using BlurHash, but that was going to involve rendering canvas elements for placeholders 1.

I want the front end to be as simple as possible 2, so I abandoned that approach and instead created a single-pixel resize of the source image which provides a simplistic average colour placeholder for each image. It does the trick.

Placeholder images

The markup

All of the necessary resizing code and markup is in a Hugo partial that renders a thumbnail for each post. Be sure that your image tags include width and height attributes so the browser lays them out correctly such that lazy loading is effective.

{{- with .Resources.GetMatch (.Params.image) -}} {{/* Resize to a single pixel
for a placeholder image */}} {{- $placeholder := .Resize "1x1" -}} {{/* Resize
to 800 pixels wide for a thumbnail image */}} {{- $thumbnail := .Resize "800x"
-}}
<img
  src="{{ $placeholder.RelPermalink }}"
  data-src="{{ $thumbnail.RelPermalink }}"
  width="{{ $thumbnail.Width }}"
  height="{{ $thumbnail.Height }}"
  class="lazyload"
/>
{{- end -}}

The main downside to this is that two resizes for each image adds a bunch of time to the site’s build process but that’s a trade off I’m willing to make.

Go forth, and embrace laziness.


  1. Or integrating React components that handle this for you↩︎

  2. The only Javascript it uses is for this lazy loading. ↩︎