Over the last few years I had to optimize images with
srcset on different websites, several times.
After each optimization I thought that I understood how the
sizes attributes of the
<img> tag works. But each time, I made some new findings about how these attributes really work and where I need to take care while implementing them. Since there are lots of things you should know before using
srcset I thought it could be useful to summarize the most tricky parts in a blog post.
A short introduction
I think most of you already heard of responsive images and especially the
srcset attribute. But to be on the same page I still would like to start with a short introduction about these two attributes:
srcset attribute is listing different resolutions of the same image from which the browser chooses the best fitting image source before loading it.
srcset="ninja-1000w.jpg 1000w, ninja-500w.jpg 500w, ..."
To calculate this, the browser assumes the image fills up the full viewport width (
100vw) by default, which means it uses the full width of the browser.
To tell the browser how much space the image really needs on our viewport, we can use the
sizes attribute. This attribute contains a comma separated list of one or more image widths for different viewport sizes.
Each entry is a combination of a
media condition and a
width. Both of these values are described in CSS pixels so we don't need to care about device pixel ratio for this. If the media condition is omitted it evaluates to true automatically (= fallback). The sizes attribute gets read from left to right. As soon as a media condition evaluates to true the width of this entry is used. So be sure to order your values correctly!
sizes="(min-width: 1000px) 50vw, 100vw"
The example above tells the browser that if the viewport is at least
1000px wide the image fills 50% of the space. If the browser is smaller, the image uses 100% of the available width.
If we combine the above two examples in an
<img> tag and request it with a (1x/non-retina) browser the result would be the following:
- Browser width:
500px-> Matching sizes width:
100vw-> Needed image size:
500w-> Chosen image:
- Browser width:
900px-> Matching sizes width:
100vw-> Needed image size:
900w-> Chosen image:
- Browser width:
1000px-> Matching sizes width:
50vw-> Needed image size:
500w-> Chosen image:
Sizes affect how the image is shown
The first thing which is important to know about the
sizes attribute is that it isn't only used to calculate the needed size, it is used as the rendered width of the image if the
width is not defined in CSS too.
This means as soon as you add the
srcset attribute to the
<img> element the image might be displayed differently.
The reason for that is when you add the
srcset attribute and omit the
sizes attribute, the browser uses the default value for it, which is
Here's an example (https://codepen.io/tschortsch/pen/LeMmyO):
As you can see, the browser stretches the image always to the matching size in the sizes attribute, as long as the
width is not defined in CSS.
This leads us to Rule #1: Always set the
sizes attribute if you use
sizes is omitted it defaults to
How the browser selects the needed image size
Short answer: We can't tell.
Long answer: Every browser has a slightly different implementation which can change with every version of the browser. This leads to Rule #2: Never assume which image size the browser will choose.
The browser just chooses the best matching image for the current size. But if the browser finds a cached version of an image from the current
srcset which is bigger than the needed size it prioritizes the image from the cache for example.
This makes it really hard to debug a
srcset. To avoid the loading of cached files you should always debug
srcsets in your browsers privacy mode.
As a result of this, it is possible that you have two exact same devices (same screen and browser size) but you get a different image size in both browsers.
Another really important takeaway from this brings us to Rule #3: A
srcset should only contain images of the same ratio.
Since you can't really decide which image is loaded in which browser size you can't use
srcset to serve different images on different sizes a.k.a. art direction (eg. 1:1 image on smaller devices, 2:1 image on larger devices). If you would like to do this you should use the
Pitfalls of the width ('w') descriptor
The width (
w) descriptor of the
srcset attribute has also some things which should be taken care of. First of all Rule #4: The width (
w) descriptor should always match the images natural width. This means if the image has a width of 500px the width (
w) descriptor should be exactly
500w. This sounds pretty easy to achieve but there are use cases where this isn't as easy.
Think of the following example: You load your images through a CDN, where you predefine all the sizes that should exist of an uploaded image. In the template you define a
srcset with all of those predefined sizes. If you do not enable upscaling (on CDN side) and upload an image which is smaller than the biggest defined size, you will get an image which doesn't fit the width (
w) descriptor in your template.
If this is the case you can get unexpected results. Let's look at this example (https://codepen.io/tschortsch/pen/rpoXvW / The problem in the example only occurs with screens that have a DPR >= 2):
If you don't have a display that has a DPR >= 2 here's a screenshot of what would happen if you had:
What is showed in the example above? As long as we don't define a CSS width, the image (which is originally 400px wide) doesn't fill up a container which is only 300px wide.
Let me explain what's happening here: We have an image container which is 300px wide. The image has a
srcset with two possible image sizes: 300w, 600w. The
300w image has a natural width of
300px (correct). But since the original image has a width of
600w doesn't return an image which is
600px wide but the original
400px image (wrong).
If this container gets loaded with a DPR >= 2 the browser requests the
600w image (2 * 300px). Since the browser doesn't know that the image behind this descriptor is smaller than
600px it displays it as it would be that size. This means it gets squeezed to half of its size (400px / 2 = 200px). And that's why we end up with this strange situation that the image (which is originally 400px wide) doesn't fill the whole 300px image container.
When we enforce the image width in CSS, like we do in the 2nd example, the (400px) image gets stretched again to fill the 300px container and everything looks like expected.
And here we are with the Rule #5: Always enforce the image size in CSS when using
As you see responsive images have some parts, you should know when implementing them. Let's quickly go through our rules again:
- Rule #1: Always set the
sizesattribute if you use
sizesis omitted it defaults to
100vw. Since the
sizesattribute has an influence on how the image is displayed, it should never be omitted. If you do so, the browser uses the default value of
- Rule #2: Never assume which image size the browser will choose. The reason for this is, that all browsers have slightly different implementation on how the correct image from a
srcsetis chosen. This implementation should be a black box for us, because it can change with every update.
- Rule #3: A
srcsetshould only contain images of the same ratio. As a result of rule #2 we never know which image is loaded by the browser. To get the same result for all visitors every image in the
srcsetneeds the same ratio.
- Rule #4: The width (
w) descriptor should always match the images real width. The browser uses the width (
w) descriptor to calculate how it should display the image. If it doesn't match the real width, we can get unexpected behaviour.
- Rule #5: Always enforce the image size in CSS when using
srcset. Since there are some use cases where rule #4 can't be achieved, we should always enforce the width of an image in CSS.
There are some really good resources to learn more about responsive images:
- Responsive images on Mozilla Developer Network: https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
- Responsive images on CSS-Tricks: https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-srcset/
- Very nicely done introduction about
<img>Element documentation: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img