Last night before bed, I was thinking about how to add a photo gallery to my Hugo site. At first, I thought about pulling in images from Instagram at build time, but quickly decided against that. Currently, Hugo builds this site in under 8 seconds, so adding functions to request images from Instagram would probably slow down the site build significantly.

Since I already have a dedicated post type for sharing photos, I decided to just loop through those posts and grab the featured images to display.

{{ $scratch := newScratch }}
{{ if hugo.Environment | eq "development" }}
{{ $scratch.Set "imgUrl" .Site.BaseURL }}
{{ else }}
{{ $scratch.Set "imgUrl" .Site.Params.cdnurl }}
{{ end }}

<div class="photo-container">
  {{ range (where .Site.RegularPages "Type" "in" (slice "photo")) }}
  {{ if eq .Params.gallery_feature true }}
  <div class="photo-item">
    <a href="{{ .Permalink }}">
      <img src="{{ $scratch.Get "imgUrl" }}uploads/{{ .Params.featured_image }}">
  {{ end }}
  {{ end }}

This method works really well because it kills two birds with one stone. I enjoy sharing photo posts, and being able to pull in the featured images from those posts means I don’t need to specify another content type for the gallery.

Awhile ago, I heard about CSS flexbox on the Syntax podcast, so I thought it would be cool to give that a try for this project. I’m not sure if what I ended up with reflects CSS best practices, but it definitely works.

The CSS below is basically all I needed. There are a few additional lines for media queries to adjust the width of .photo-item.

.photo-container {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  margin: 3rem auto;
  position: relative;

.photo-container img {
  margin: 0;
  width: 100%;

.photo-item {
  width: 20%;

Imgix, the service I use for on-demand image optimization and manipulation came in very handy for this project.

I built my srcset URLS for responsive images like this.

{{ $scratch.Get "imgUrl" }}uploads/{{ .Params.featured_image }}?w=1024&ar=3:2&fit=crop&auto=format&q=50&px=32&blur=200 1024w,
{{ $scratch.Get "imgUrl" }}uploads/{{ .Params.featured_image }}?w=768&ar=3:2&fit=crop&auto=format&q=50&px=32&blur=200 768w,

Notice the ar and fit parameters, which automatically crop images to a 3:2 aspect ratio in realtime! This is very important because I use a variety of cameras. My Leica Q2 and Fujifilm XPro-2 use a 3:2 aspect ratio by default, while my iPhone uses an aspect ratio of 5:4. The image gallery wouldn’t look very nice if the images had different aspect ratios.

After some testing, I wanted to find a way to make the gallery load faster. Unsurprisingly, Imgix came to the rescue again. I added two additional configurations to the URL – px=32&blur=200. With these two parameters, images are blurred and distilled into 32 pixel blocks.

Like this…

Next, I’m using the JavaScript below to swap out the srcset URLs with data-srcset URLs without the px=32&blur=200 parameters.

function init() {
  var imgDefer = document.getElementsByTagName('img');
  for (var i=0; i<imgDefer.length; i++) {
  if(imgDefer[i].getAttribute('data-srcset')) {
window.onload = init;

This configuration results in small “placeholder” images loading first. After the full-resolution images are downloaded, JavaScript automatically swaps them out. So, the placeholder image above ends up looking like this.

If you’re interested in checking out the photo gallery, you can find it here. I’ll probably add a few more features to it over the coming weeks, but I think it’s in pretty good shape for now. If you have any questions about how the photo gallery works, feel free to reach out to me on Twitter or send me an email!