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.
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.
-
Or integrating React components that handle this for you. ↩
-
The only Javascript it uses is for this lazy loading. ↩