Responsive Images: Improving performance by letting the browser do the work
One of the most impactful ways to improve the performance on your site is by optimizing your images. Images account for 60% of the bandwidth needed to load the average webpage. The less you have to download for your images, the sooner the page is presentable and the user can begin interacting with it.
We want to serve appropriate images to all of our users, like small images on slower connections and small screens, and large, high-quality images on fast connections and high-resolution screens.
Luckily, all modern browsers support a few nifty HTML features to help us optimize our images for whatever device your users are visiting your site on.
Let the browser choose the best image
Using the features explained in this article, we can let the browser do the heavy lifting in deciding which image is the best to use for a given situation. Modern browsers are highly optimized to do this.
We provide the browser with a few key pieces of information:
- A set of image sources and their corresponding widths via
srcset
- A list of media queries with the size of the image slot at those viewports via
sizes
Combining this information with what the browser already knows:
- Viewport size
- Device pixel ratio
- Currently cached images
- Connection speed
It can then make a decision on which image to use.
The srcset attribute
The srcset
attribute allows us to provide a comma-separated list of image sources with corresponding widths to an image tag. This lets the browser make a decision as to which image is the most appropriate to download.
We provide a few things for each member of a srcset:
- The file path to the image.
- The actual pixel width of the image
<img src=”https://dummyimage.com/1080” srcset="https://dummyimage.com/375 375w, https://dummyimage.com/500 500w, https://dummyimage.com/786 786w, https://dummyimage.com/900 900w, https://dummyimage.com/1080 1080w, https://dummyimage.com/2400 2400w" />
You’ll notice the w
unit on the size—this refers to the width of the image in pixels.
Note: Keep in mind we want to continue to include a src
attribute here. Browsers that don’t support srcset
will default to src
.
Device pixel ratio
Understanding device pixel ratio (DPR) when thinking about the image sizes in the srcset
and sizes
attributes is important. The device pixel ratio is the ratio of how many pixels on the hardware make up a single CSS pixel, so a 1:1 DPR screen has a single hardware pixel for every CSS pixel.
Originally retina displays were considered a 2:1 DPR, but now this descriptor has become vague as there are many different screens out there ranging from a 1:1 to a 4:1 DPR.
To name a few:
- 1.0 - Basic desktop monitor
- 2.0 - iPhone 8
- 2.625 - iPhone 8 plus
- 3.0 - iPhone X
- 4.0 - Sony Xperia Z5
Keep in mind, the image sizes in both the src
and srcset
attributes are referring to CSS pixels.
Note: You can read the DPR of a device with window.devicePixelRatio
.
The sizes attribute
Using the sizes
attribute we can explicitly tell the browser what the slot width of the image will be at specific media condition.
Let’s go into what the sizes
attribute looks like:
<img src=”https://dummyimage.com/1080” sizes="(min-width: 769px) 25vw, (min-width: 376px) 50vw, 100vw" srcset="https://dummyimage.com/375 375w, https://dummyimage.com/500 500w, https://dummyimage.com/786 786w, https://dummyimage.com/900 900w, https://dummyimage.com/1080 1080w, https://dummyimage.com/2400 2400w" />
The sizes
attribute also takes a comma-separated list consisting of the media condition and what size the image slot is in either an absolute width (px or em) or a relative width (vw). Note that you can not use percentages for the slot width. The media condition provided can use and, or, and not logical operators as well, but doesn’t allow media types like screen or print. These media conditions are evaluated from top to bottom and the browser will choose the slot width of the first condition that evaluates to true.
The sizes
attribute above is saying a few things:
- At a viewport width greater than or equal to 769px the image slot is 25vw
- At a viewport width greater than or equal to 376px and less than 769px the image slot is 50vw
- At any other viewport width that does not meet the above media conditions the image slot is 100vw
Note: If using React, make sure to place sizes
before srcset
when listing the attributes. Because React uses Element.setAttribute()
, adding srcset
before sizes
can cause certain browsers to download two images. In our work, we specifically saw this behavior on Safari.
Putting it all together
To see the real world implications of how these attributes might help us improve our site’s performance and create a better user experience, let’s take a look at a hypothetical example.
See the codepen here: https://codepen.io/formidablelabs/full/MWWYoLd
First, let’s make some assumptions:
- Our user is on a 2.0 DPR screen
- Our user is on a fast connection
- The browser has no images cached
Our example is a layout that has four columns on screens larger than 768px, two columns on screens that are 376px-786px, and a single column on screens 375px and smaller.
Tablet Layout
Mobile Layout
With this layout, we can take a look at how these attributes affect what image is downloaded.
Let’s start with a just a regular image tag with a src:
<img src="https://dummyimage.com/1600" />
Just a src, no sizes or srcset:
Device width | Image slot width | Image downloaded |
---|---|---|
1920px | 473px | 1600px |
768px | 368px | 1600px |
375px | 304px | 1600px |
As expected, the 1600px image will be downloaded at every device width. This image is more than double the image size at each of these device widths—way too big.
Now let's take a look at an image with a src
and a srcset
property:
<img src="https://dummyimage.com/1600" srcset="https://dummyimage.com/375 375w, https://dummyimage.com/600 600w, https://dummyimage.com/786 786w, https://dummyimage.com/1080 1080w, https://dummyimage.com/1600 1600w, https://dummyimage.com/2400 2400w" />
src and srcset, no sizes:
Device width | Image slot width | Image downloaded |
---|---|---|
1920px | 473px | 2400px |
768px | 368px | 1600px |
375px | 304px | 786px |
This time on the two larger screens we get an even larger image than using just a src
. But, isn't srcset
supposed to help us download more efficient images? Well, it does... sometimes. The reason the browser downloaded these images is because, without a sizes
attribute, the browser assumes that the image slot is 100vw.
Let's look at what happened on the tablet-sized device a little closer. The browser knew that the device width was 786px wide at a 2.0 DPR. It assumed the image was 100vw or 786px so it took that and multiplied it by 2.0 (DPR), took a look at the srcset
and found the best-suited image.
The browser slightly upscaled the image and decided the 1600px image was the most appropriate to download.
You should use sizes
in tandem with srcset
to get the most optimized images, whenever your image slot width deviates from 100vw at any device width.
Let's see how we can use sizes
to get the best image for the job, take this image:
<img src="https://dummyimage.com/1600" sizes="(min-width: 769px) 25vw, (min-width: 376px) 50vw, 100vw" srcset="https://dummyimage.com/375 375w, https://dummyimage.com/600 600w, https://dummyimage.com/786 786w, https://dummyimage.com/1080 1080w, https://dummyimage.com/1600 1600w, https://dummyimage.com/2400 2400w" />
The sizes
on this image describe our layout:
- On a screen > 768px our image is 25vw
- On a screen > 376px and < 769px our image is 50vw
- On any other screen that doesn't meet the conditions above, our image is 100vw
Note: The reason we've decided to use vw
instead of px
to describe the image slot in sizes
is because we're using css grid and the px
width of the slot will fluctuate depending on the device, and there is a small amount of gap between our grid. Using vw
will give the browser a good estimate to work with.
src, srcset, and sizes:
Device width | Image slot width | Image downloaded |
---|---|---|
1920px | 473px | 1080px |
768px | 368px | 786px |
375px | 304px | 786px |
Since the browser now knows what width the image slot is, it can use that to calculate the image to download instead of the entire viewport width.
On the largest screen:
- The viewport width is 1920px and 25vw (what was described in the
sizes
media condition for this device width) at that viewport width is 480px.
The browser upscales to the most appropriate image and decides the 1080px image is a good fit. This is over 2x smaller width than the image downloaded using srcset
alone!
On the tablet:
- The viewport width is 768px and 50vw at that viewport is 384px.
The browser downloaded the 786px image from the srcset
—nearly spot on!
On mobile:
- The viewport width is 375px and 100vw is well, 375px
This size meets the default media condition in sizes
of 100vw, the browser knows the image is full width on mobile devices, so it downloads the 786px image. This is over 2x smaller width than the image we downloaded using just a src
.
Gains 💪
Now, with these dummy images the actual download size benefits are pretty small because they're kinda boring (no offense to the crew at dummy image—this API is great!). Let's take a look at this very good boy to see some image size gains.
Take the example above where the user was on a mobile device where a srcset
and sizes
attribute was added to the image and compare it to just using a src
from the first example.
The photo above is 786px wide, the size downloaded using srcset
and sizes
. The size of this photo is 80.1kb. The 1600px version of this photo, the image width downloaded using just src
, is 254kb! Using srcset
and sizes
saved us more than 3x the bandwidth for that photo!
Conclusion
With a single photo, this difference isn't too noticeable, but if you're using a lot of photos on your site these differences can add up quickly and drastically improve your load times. Using these attributes can help your website be more accessible to users on mobile and on slow connections, while simultaneously delivering the highest quality image to users on high resolution screens and fast connections.
Big thanks to my coworkers Amy Dickson, Chris Bolin, and Steven Musumeche for reviewing this article.