Image processing may seem complicated at first but it’s actually easy and definitely worth implementing since it’ll help you decrease page load times. As you probably know, we don’t want to load raw images with huge sizes for small thumbnails or blog-posts. We want to load a small image if we are going to display it in a small container or on small devices like smartphones. Yet, we don’t want to resize images, rename and store them manually. No one would do that. Thankfully, Hugo has a feature called image processing.
Before, going further you must know that you can’t store your images under /static
folder anymore. To be able to use image processing, an image must be a resource
. What is a resource? Hugo has two types of resources. One is called ‘Page Resource’, page resources are stored within the page’s directory. So, you create a folder for each post and rename the post content as index.md
and keep the images in the same folder. The second one is called ‘Global Resource’ which is stored in /assets
.
I moved all images under /static
to /assets
folder. Of course, you can create sub-folders inside the assets folder. I would’ve stored the images for this post like this: /assets/images/posts/2021-01-23-image-processing-hugo/
.
Resize An Image
First, I will start with a simple example. Let’s say we want to create a fixed-sized smaller image from a bigger raw image. We’ll use it as a thumbnail for all devices. We won’t bother with the device size for now.
{{ $imagePath := printf "images/thumbnails/%s" .Params.thumbnail }}
{{- $image := resources.Get $imagePath -}}
<img src="{{ ($image.Fill "320x500 q75 Center").RelPermalink }}"/>
- Get the image path somehow. I set the image I wanted to use for a post in it’s front-matter.
thumbnail: blablabla.jpg
- The second line looks up to the given path and gets the resource(image) if there is any match. Don’t forget that this looks under
/assets
folder. So, the path you give should be relative to the assets. - The third line resizes the image 320x500 with quality 75 and use its relative permalink.
Use srcset
In this section, we will use srcset
. Source set requires a set of images instead of one image. So, we will need to resize an image multiple times. Then, we’ll give use all these images in html file and let the browser decide which one to use. The browser will load the appropriate size according to device size.
{{ $banner := resources.GetMatch "pathToYourImage" }}
{{ $tiny := ($banner.Fill "480x280 q90") }}
{{ $small := ($banner.Fill "768x280 q90") }}
{{ $medium := ($banner.Fill "1024x280 q90") }}
{{ $large := ($banner.Fill "1366x280 q90") }}
<img
srcset="
{{- with $tiny.RelPermalink -}}{{.}} 480w{{- end -}}
{{- with $small.RelPermalink -}}, {{.}} 768w{{- end -}}
{{- with $medium.RelPermalink -}}, {{.}} 1024w{{- end -}}
{{- with $large.RelPermalink -}}, {{.}} 1366w{{- end -}}"
src="{{ $banner.RelPermalink }}"
alt="banner-image">
Image Processing in Blog Posts
Normally, to use an image in our markdown file we use standard syntax ![]()
.
Even though, I don’t like to go out of plain markdown, I had to create a shortcode for providing more features; like captions, css classes, image-processing and javascript libraries like nanogallery2.
A shortcode is a code snippet in Hugo, that takes some input process it and returns an output just like a function.
So, I have got a shortcode named “img” and I call it in markdown files, like this: {\<img src="" >}
.
In this shortcode, I didn’t use srcset
. It simply resizes the given image to a pre-defined fixed size and uses nanogallery2 (a javascript library) to display the original image on click. I’ll share my nanogallery2 experience in another post, since it feels like another topic. Yet, if we want to display an image in smaller size without any javascript features.
We can do so, by simply creating a shortcode.
{{ $image = resources.GetMatch (.Get "src") }}
{{/* Generate Thumbnail */}}
{{ if ge $image.Width $image.Height }}
{{ if ge $image.Width "600" }}
{{ $.Scratch.Set "thumbnail" ($image.Resize "600x q90") }}
{{ else}}
{{ $.Scratch.Set "thumbnail" $image }}
{{ end }}
{{ else }}
{{ if ge $image.Height "450" }}
{{ $.Scratch.Set "thumbnail" ($image.Resize "x450 q90") }}
{{ else }}
{{ $.Scratch.Set "thumbnail" $image }}
{{ end }}
{{ end }}
<img src="{{ ($.Scratch.Get "thumbnail").RelPermalink }}"/>
Conclusion
Processing images will increase the build time but Hugo is pretty fast and you’ll not notice it probably. Good thing is that Hugo is caching these results and won’t start all over again when you are working in your local environment. So, it shouldn’t affect your workflow. You may see an increase in build time on the server (GitHub, Netlify, GitLab, or whatever service you are using).
I don’t have a scientifically right benchmark but this should give an idea: I’ve around 50-60 image files which are ~25MB in total and build time is increased from 30-36 seconds to 40-50 seconds which is very fast. Local build times took less than 7 seconds if I clean the cache and less than 1 second with the cache. Just amazing!