Reduce loading times of image-heavy sites with `srcset`

— An article about: - Images - Performances

This is the first article of a small series about techniques for keeping the loading times of image-heavy websites in check. Lots of images mean more KB to download for the browser. And in turn, longer loading times.

An obvious answer to the problem would be to cut down the number of images. But on some websites (this portfolio, for example) images are one of the main part of the content. Let’s see what we can do to keep things light.

Use images at the right resolution in the right format

For the first download at least, it pretty much comes down to that (you can set up caching rules to make future loads faster). Let’s leave the “right format” to its own future article (between vector or raster, optimised images, new formats like WebP and the <picture> element there’s a fair bit to explore). This article will focus on the “right resolution” bit.

Loading an HD image when it’s only displayed 200px wide has several issues:

  • it makes users wait, augmenting the risk of abandoning their visit
  • it needs more bandwidth, making users pay unnecessarily for their visit
  • it requires the browser to resize it heavily, making their device use more battery

This article will also only deal with content images (<img> tags). A future one will deal with those that are in the CSS.

The problem

This “right resolution” is not quite easy to pin, with websites getting accessed through a wide variety of devices:

  • with different screen sizes,
  • and different pixel density.

This means there won’t be a unique “right resolution”. An image that 400px wide on a phone screen might get a much larger share of the screen on a desktop monitor.

Historically, the <img> tag only had the src attribute to provide the image URL. So which one to choose? A low res image will look terrible when scaled up on large displays. But a large image will blow up the loading times 🙁

srcset to the rescue

What if we could let the browser choose? Give it a list of image URLs with different resolutions and let it pick which is the most appropriate to load. It’s well aware of the device that’s using it after all. Way more than the server it’s requesting the image to.

This is exactly the role of the srcset attribute on the <img> tag. Inside, you can list additional URLs the browser can load instead of the src one. Each is associated with a width or pixel density which will help the browser make its choice.

To pick an image with the appropriate width, though, the browser will need a bit more help. Viewing the website in a 1080px wide browser doesn’t mean all images will be 1080px wide. So we need to add some more hints with the sizes attribute. It contains a list of media queries (just like in CSS) to tell the browser which image to look for in the srcset depending on the situation. First match goes! (unlike in CSS).

This leads to markup that looks like that:

<img 
  src="/images/funnycat-200x150.jpg"
  height="150"
  width="200"
  srcset="/images/funnycat-400x300.jpg 400w,
          /images/funnycat-800x600.jpg 800w,
          /images/funnycat-800x600.jpg 3x"
  sizes="(min-width: 1600px) 800px,
         (min-width: 800px) 400px,
         200px">

With this, the browser can say “hey! my screen is between 800 and 1600px wide. I can use the funnycat-400x300.jpg image instead of the src one”.

A couple of things to note:

  • the width in the srcset are suffixed by w, but they’re actual CSS length in the sizes attributes. Easy to mix them up, so watch out! On the plus side, that means you can calc() things in the sizes.
  • the src is not necessarily the smallest image. A few browser don’t support srcset (IE, Edge 15 and before, and Opera Mini as I’m writing this). They’ll just use src, so a compromise will be needed between quality and load times there.
  • to follow the specs, the original src image must not appear in the srcset (nor should there be duplicate entries). Doesn’t seem to break the behaviour, just make the markup invalid.
  • to follow the specs again, if there are widths in the srcset, the sizes attribute must be present. Didn’t seem to break things either, just use the whole viewport width to pick the image width.

Adding the srcset attribute to your site

From a practical point of view, implementing srcset in your design is best done once the CSS has been finalised (or at least finalised enough for the image sizes to be set in stone).

Making an image width inventory

The first step is making an inventory of the image sizes for the different pages of your website at different screen sizes. Unfortunately, I don’t know of any crawler that does that automatically (that would be ideal, if you know of one, please let me know!), so it’ll have to be a manual process.

There’s no need to go through each page, though. Only each “template”. And equally, there’s no need to go through the whole width spectrum as you know the breakpoints in your CSS.

Some images might also be percent or viewport unit based. For those, you’ll need to decide of size increments to adapt the served asset to the final design, just like more “fixed” images.

Aggregating the sizes

The inventory might leave you with quite a collection of sizes. In an ideal world, you’d provide each <img> tag its very own list of URLs and sizes. That can quickly lead to a maintenance overhead:

  • generating and storing/caching of the images,
  • filling the srcset and sizes attributes accurately.

To ease the burden a bit, you can regroup the sizes into similar sizes to reduce the number of images you have to handle. This might mean a few more pixels to download as the price of an easier maintenance. You’ll have to weight if the trade-off is worth it.

Plan for different pixel densities

We now have a list of the images sizes we’ll be implementing for our design. This is time to think of screens with high pixel density and not forget to plan for 2x and 3x versions of these images.

With a little luckplanning, these 2x and 3x might even match some of the other sizes, which will lower the number of images to generate.

Generate the images and write the srcset and sizes attributes

These last two part will very much depend on how your website is set up.

For the generation, exporting every size from your favourite image editor will be tedious. I’d recommend leaving that to your CMS, some scripts or a SaaS platform.

Once all your images are ready, you can add the attributes to the markup and voila! Your users will load the appropriate image for their screen!

In the end

The srcset attribute is a great way to shave some KB of download for your users by making your website load just the right image for the screen it’s viewed in. Of course, associating the screen size to the bandwidth available (and in turn loading time) is not a perfect correlation. But it’s a good place to start.

Hope this article was helpful to you. If you want to chat about it, feel free to get in touch here or on Twitter.