published on 23.01.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 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.

{{ $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.

{{ $banner := resources.GetMatch "pathToYourImage" }}

{{ $tiny := ($banner.Fill "480x280 q90") }}
{{ $small := ($banner.Fill "768x280 q90") }}
{{ $medium := ($banner.Fill "1024x280 q90") }}
{{ $large := ($banner.Fill "1366x280 q90") }}

    {{- with $tiny.RelPermalink -}}{{.}} 480w{{- end -}}
    {{- with $small.RelPermalink -}}, {{.}} 768w{{- end -}}
    {{- with $medium.RelPermalink -}}, {{.}} 1024w{{- end -}}
    {{- with $large.RelPermalink -}}, {{.}} 1366w{{- end -}}"
src="{{ $banner.RelPermalink }}"

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 }}"/>


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 with commit 3be25fb.
#hugo #static-site
Next episode:
NanoGallery2 with Hugo
published on 24.01.2021

Some time ago, I wanted to display images in a better way on my Hugo website. I searched for existing Hugo themes for photography and gallery tags. Can’t say I find much. Then, I met a javascript library called nanogallery2 which is using another javascript library as an image viewer …

published on 10.07.2022

Previously, I’ve published a blog post about deploying static content on heroku with basic authentication. The main purpose was to get basic auth for a freely hosted static website. In that post, we hosted the source code on GitLab and configured a CI/CD pipeline to render the static content …

published on 28.05.2022

Each git commit has a field called Author which consists ‘’ and ‘’. We usually set these variables once, after installing git, with git config --global so that each repo gets the variables from the global definition. We can also set them locally for a …

published on 25.05.2022

In this post, I’ll first walk through hosting static content with basic authentication. Then, we’ll look into deploying to Heroku using GitLab Pipelines, more specifically deploying a certain sub-directory within the project instead of pushing the whole project. Also, I’ll share …

published on 17.04.2022
edited on 15.07.2022

Önceki bölümde, markdown formatını LaTeX formatına dönüştürmek için kullanılan Pandoc yazılımından bahsetmiştik. Şimdi konuyu bir adım daha ileri taşıyıp ve bookdown’a geçiyoruz. Bookdown; Rmarkdown kullanarak teknik dökümanlar, kitaplar yazabilmemizi sağlayan, Yihui Xie tarafından yazılmış …

published on 10.04.2022

I’ve been using WSL-2 on Windows for over a year. It’s very useful because some Python packages are just a headache to install on Windows. Also, docker. It’s just better on Linux. Yet, WSL-2 can also be problematic. I remember trying a dual-boot setup when things just went way too …

published on 03.03.2022

In this post, I’ll share how to install geopandas and some other gis related packages on Windows. If you are on Mac or Linux you can probably just pip install those without any issue. I usually had to do a google search every time I wanted to install these packages on Windows environment. Of …