Image processing in Hugo

introduction into image processing in Hugo

In this guide we share our partials and shortcodes that we use for image processing for both normal as background images.

Image shortcode with image processing and columns

First, let us share a nice image shortcode with built-in image processing and css grid styling for multiple images in a row. This image shortcode automatically saves images for various screensizes. It takes an image from the Hugo page bundle that has a part of the name that you enter in it. When you add multiple image-names, or parts of the names, it will automatically add them as columns. You can also choose the number of columns by adding a number in the shortcode at any place.


This shortcode is styling agnotistic. It has standard styling inside, so you can use it in any of your Hugo themes.
You can insert the shortcode in your markdown like this:
{{< image img1 img2 img3 >}}

This would give 3 images in 3 responsive columns. The images should be in the images folder as a pagebundle. Here's the result:
Image processing in Hugo guides
Image processing in Hugo guides
Image processing in Hugo guides

First, let's share the code. Below, we will explain how it works.
{{/* for automatical columns, apply like this <image img1 img2 img3> .You can add an infinite amount of images */}}
{{/* If you want to determine columns yourself, add an integer between 1 6 somewhere in the shortcode, e.g. for two columns <image 2 img1 img2 img3 img4> */}}

{{ $page := .Page }}
{{ $images := slice }}
{{ $class := "" }}
{{ $numberOfCols := 1 }}
{{ $cols := slice "oneCol" "twoCols" "threeCols" "fourCols" "fourCols" "sixCols" "sixCols"}}

{{ range $index, $value := .Params }}
      {{if eq (printf "%T" $value) "string" }}
          {{ $images = $images | append $value }}
      {{ end }}
      {{if eq (printf "%T" $value) "int" }}
          {{ $numberOfCols = $value }}
          {{ $class = index $cols (sub $numberOfCols 1) }}
      {{ end }}
{{ end }}

{{ if eq $class "" }}
  {{ if le (len $images) 3 }}
    {{ $numberOfCols = len $images }}
    {{ $class = index $cols (sub $numberOfCols 1) }}
  {{ else }}
    {{ $numberOfCols = 3 }}
    {{ $class = "threeCols" }}
  {{ end }}
{{ end }}

<style>
    .grid {
      width: 100%;
      display: grid;
      grid-auto-flow: row;
      justify-items: stretch;
      justify-content: stretch;
      grid-gap: 10px;
      grid-template-columns: repeat(12, 1fr);
    }
    .oneCol, .twoCols, .threeCols {
      grid-column: span 12;
    }
    .fourCols, .sixCols {
      grid-column: span 6;
    }
      @media (min-width: 768px) {
        .twoCols, .threeCols {
          grid-column: span 6;
        }
        .fourCols, .sixCols {
          grid-column: span 3;
        }
      }
      @media (min-width: 992px) {
        .twoCols {
          grid-column: span 6;
        }
        .threeCols {
          grid-column: span 4;
        }
        .sixCols {
          grid-column: span 2;
        }
      }
    .pogoimage {
      width: 100%;
    }
</style>

<div class="grid">
{{ range $images}}
    {{ $image_name := .}}
    {{ $image_path := "" }}
    {{ $resource_page := $page.Page }}
      {{ if or (eq $page.Kind "taxonomy") (eq $page.Kind "term")}}
        {{ $image_path = printf "*%s*" $image_name}}
      {{ else }}
        {{ $image_path = printf "%s/*%s*" "images" $image_name }}
      {{ end }}
    {{ $sizes := (slice  "360" "480" "800" "1200" ) }}
    {{ $image := ($resource_page.Resources.ByType "image").GetMatch $image_path}}
    <div class= "{{$class}}">
        {{ with $image }}
          {{ $filename := path.Base .Name }}
            <img class="pogoimage" sizes="{{ printf "%svw" (string (div 60 $numberOfCols) ) }}"
              srcset='
              {{ range $sizes }}
                {{ ($image.Resize (printf "%sx" .)).RelPermalink }} {{ (printf "%sw" .) }},
              {{ end }}'
              src="{{ ($image.Resize "640x").Permalink }}"
              class="lazyload image"
              alt="{{ printf "%s %s %s" $page.Title $page.Type $page.Summary }}"
            >
        {{end}}
    </div>
{{ end }}
</div>

Ok, let’s take it apart. The code consists of three parts.

  1. Determining the number of colums
  2. Applying the right styling
  3. Serving the image

The first and second part do not need special explanation. The code explains itself. There are enough examples for the third part where image sizes are set and applied to the srcset of the img tag, so look at the links below.
Read the Hugo documentation here
Here is another nice guide about image processing in Hugo

The way the image is found as a resource is worth giving more explanation.

Getting the image resource based on a part of the name

Let’s look at that specific piece of code:

{{ range $images}}
    {{ $image_name := .}}
    {{ $image_path := "" }}
    {{ $resource_page := $page.Page }}
      {{ if or (eq $page.Kind "taxonomy") (eq $page.Kind "term")}}
        {{ $image_path = printf "*%s*" $image_name}}
      {{ else }}
        {{ $image_path = printf "%s/*%s*" "images" $image_name }}
      {{ end }}
    {{ $image := ($resource_page.Resources.ByType "image").GetMatch $image_path}}

This code ranges over the image names that are given in the shortcode. It looks whether the page is a term/taxonomy page or not. The images in these type of pages are in the same directory as the index.md of the page. That’s why the image_path does not contain the /image/ directory. For other pages the shortcode expects an image directory as pagebundle.

Then, when the image path is correctly set, it matches the image ont the image_path with wildcards in this line. Then, the image resource can be used to resize.

   {{resource_page.Resources.ByType "image").GetMatch $image_path}}

Background images as processed resources.

We will add this part in a couple of weeks.