Minimizing reflow (caused by responsive images with lazy load) using the filter feature

Found a weird way to mitigate reflow (caused by responsive images with lazy load) using the filter feature and decided to share.

Basically, I’m using the new filter feature to generate a “blank” placeholder. Instead of being a 1x1 GIF/PNG in base64, it’s “proportional” to the image (where I only know the width):

{{ $placeholder := (.Resize "8x") | images.Filter (images.Contrast -100) }}
  
{{ $encoded := ($placeholder.Content | base64Encode) }}

Where the responsive img will something like:

src="data:image/jpeg;base64,{{ $encoded }}"
data-src=...
data-srcset=...

As the CSS, a simple img { width: 100% } or whatever the size you want to use should be enough. It’s just to enlarge the placeholder to the proportion of the image yet to load.

So far it was the laziest way I found to minimize reflow caused by images with variable height. It should be compatible with most lazy load JS libs out there. No inline style or more than a simple CSS rule.

To reduce the reflow even more (or avoid completely) you could increase the placeholder width, but also getting a longer base64.

And I’m not entirely sure, but maybe the size could be reduced with a (not available yet) convert function, using a PNG or GIF here.

2 Likes

Cool technique.

This has the benefit of eliminating reflow without resorting to the somewhat limiting CSS padding-ratio calculation (which works best for images that have the same ratio).

I’m currently testing it out.

Thank you.

However note that in the next version of Hugo you will be able to convert an image to another format with this syntax: $placeholder := (.Resize "8x gif") or (.Resize "8x png")

The above is currently available in Hugo DEV since https://github.com/gohugoio/hugo/pull/6354 was merged and you can test it out if you can compile Hugo from source.

2 Likes

use the size attributes to reduce reflow and resorting. I generate previews of a fixed size and this works very well. Sample:

{{range .Resources.Match "gallery/*" -}}{{ $image := .Fit "360x240" }}{{ $preview := .Fit "36x24" }} 
      <a href={{.RelPermalink}}><figure><img loading=lazy src={{ $image.RelPermalink }} 
        width={{ $image.Width }} height={{ $image.Height }} 
        style="background: url(data:image/jpeg;base64,{{ $preview.Content | base64Encode  }}); 
        background-size:100% 100%;"  /></figure></a>
{{- end }}

The embedded preview is 1/10 in width and height

1 Like

This thread reminded me to implement this:

4 Likes

Nice!

For some reason the GIF output is bigger than the PNG. Below I’m using “160x png”:

The data URL here is almost 3x shorter when compared to JPEG:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAABkCAIAAACO1KzYAAABBElEQVR4nOzRAQkAIBDAQBGDf3RTiDDuEgx2ZmbRtX8H8JbBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcQbH3QAAAP//PJoCS3Isi7UAAAAASUVORK5CYII=

Far better than before.

1 Like

Just saw this and thought it would be interesting to post here: