How to prevent CLS in Hugo

"What is Cumulative Layout Shift (CLS) and why does it matter?" This is probably my favorite example.

Preventing CLS requires setting explicit width and height attributes on all pictures. Here are the steps I took to optimize the images in hugo using the render hooks feature.
- Get the raw image data from a URL
- Decode the image data
- If available, set the width & height attributes
- Otherwise, fallback to the default template without the attributes
Here’s the complete code snippet.
<!-- layouts/_default/_markup/render-image.html -->
{{ $alt := .Text }}
{{ $src := .Destination }}
<!-- Load & decode image -->
{{ $img := os.ReadFile $src | resources.FromString $src }}
{{/* Set Dimensions on Raster Images */}}
{{ if ne $img.MediaType.SubType "svg" }}
<img
alt="{{ $alt }}"
src="{{ $src | safeURL }}"
width="{{ $img.Width }}"
height="{{ $img.Height }}"
/>
{{ end }}
{{/* Svgs & Images that failed to load */}}
{{ if or (not $img) (eq $img.MediaType.SubType "svg") }}
<img src="{{ $src | safeURL }}" alt="{{ $alt }}" />
{{ end }}
For this render hook to work, images must be stored in the /img
folder
relative to the project root with this configuration file.
# config.yaml
module:
mounts:
- source: static
target: static
- source: img
target: static/img
Further development
- Suport Remote Image sources.
- Support different local image sources eg:
/content
,/static
or/assets
. - Multiple image formats & sizes.