Dance Computer, Dance

by Ray Grasso

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.