published on 23.01.2021
edited on 11.06.2021

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.

The Fuji Reflects in Lake Kawaguchi
The Fuji Reflects in Lake Kawaguchi

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.

1
2
3
  {{ $imagePath := printf "images/thumbnails/%s" .Params.thumbnail }}
  {{- $image := resources.Get $imagePath -}}
  <img src="{{ ($image.Fill "320x500 q75 Center").RelPermalink }}"/>
  1. Get the image path somehow. I set the image I wanted to use for a post in it’s front-matter. thumbnail: blablabla.jpg
  2. 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.
  3. 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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
   {{ $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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{{ $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!

You are reading the 18th of 19 episodes in Hugo.
Published on 23.01.2021 by Mert Bakır. Last update on 11.06.2021 with commit 017b782.
hugo
#hugo #web-dev
Next episode:
NanoGallery2 with Hugo
published on 24.01.2021
edited on 11.06.2021

Some time ago, I wanted to display image galleries on my Hugo website and searched for Hugo themes for photography and gallery. I can’t say I find much. Then, I met with a javascript library called nanogallery2 which is using another javascript library as an image viewer lightbox2. In this …

published on 29.11.2020
edited on 05.12.2020

Plotly is a visualization library that allows us to write code in Python, R, or Julia and generates interactive graphs using Javascript. So, we don’t have to deal with Javascript. You can checkout Plotly gallery, there are interesting works. Anyway, last week, I’ve started learning …

published on 18.09.2020
edited on 13.02.2021

First of all, if you don’t know about data files, you may want to start reading from data files in hugo. This post is about a solution for a very particular problem. How can we use “group by” for the data from data files? Let me clarify with an example. I was creating a single page …

published on 30.08.2020

The question is clear. We need to pass variables or arguments in Hugo’s partials. We usually use only the dot when calling partials. {{ partial "foo.html" . }}. The dot means the current page’s context. All it’s variables like .Title, .Permalink, .Content and all others …

published on 31.12.2020
edited on 26.06.2021

I’ve, recently, published a blog post called Perfect Workflow for Publishing Python Notebooks. I talked about some of the benefits of using Rmarkdown and reticulate. In this post, I’ll try HTML widgets and explain how we can embed those in our blog post using nothing but R. […] 1 …

published on 05.12.2020

Resume A4 is a side project of mine. It’s one page Hugo Theme that allows you to write your resume in YAML format and keep track of it using git. Also, you can publish it online as a static site using GitLab, GitHub Pages, Netlify, or some other service you are familiar with. A few months …

published on 30.11.2020

I’ve been searching for a good workflow for publishing Jupyter or RMarkdown Notebooks as static blog posts. I think I’ve found the optimal solution for my use case. In this post, I’ll explain my workflow and why chose this way with examples. […] In reality my main purpose to …