Applying srcset: choosing the right sizes for responsive images at different breakpoints
How to choose the correct pixel width of your images based on common viewports and the size of the image in your design.
There are several great guides online that explain how to use responsive images, both using the srcset and sizes attibutes of the <img> tag and the more sophisticated (and sometimes overly complicated) <picture> tag. They usually explain the technical details of the two methods of implementing responsive images, and their respective benefits. But they rarely give you advice on how to actually use the tags, especially in terms of choosing the image sizes to actually insert in the tags.
This article arises from a personal need: lately I have been working as lead front-end developer or lead developer on high traffic websites where images need to be optimized both for performance and for bandwidth reasons. The sites, as is usually the case lately, are full of big images, often used in full-page mode, sometimes as a background for text, but also as the predominant content on the page. Images can take 100% of the page width, but depending on the designers’ choices they can take a variable percentage of the screen, and often that percentage varies according to the current breakpoint.
While applying the srcset attribute on many images on the websites I was not 100% satisfied with my choice of images sizes, which had been done relying on the few pieces of advice available on the web and by manually testing on a range (several, actually) of browsers. I decided I wanted to act on the basis of some analysis of empirical data instead of an abstract understanding of the problem.
There are several factors involved in managing responsive images, some of which are:
- End-user screen size
- End-user browser capabilities
- End-user screen resolution
- End-user bandwidth (I imagine some mobile browser might take that into account when requesting images, I actually don’t know whether they do or not)
- Width of image in the layout (percentage of screen real estate occupied by image)
- Different width of the image depending on breakpoint (for example, a product image in an e-commerce website might occupy less than 33% of the screen on desktop resolutions if the products are distributed in lines of 3, and maybe 50% or 100% on mobile where they are distributed in lines of 2 or one under the other).
- Different image shape depending on breakpoint, where an image is completely different depending on the layout (for example, show the photo of a full person on desktop, and the face only on mobile). This is a fairly extreme case, and often requires a very tight team of designers/developers to put in practice, as generally images have to be cut “by hand” to match each layout. They generally require the <picture> tag, and are (according to my work experience) a rarer case, and they will not be treated here.
Given that with the srcset attribute browsers have total freedom in choosing which images to display, I decided to base my decisions on actual data from real browsers, and not only on calculations. We do not know which version of each image the browsers will actually download until they do.
This section describes the methodology used to retrieve real browser data on responsive images, and as such it will be fairly boring. If you’re interested in the results only, feel free to skip to the next section.
To have real data from real browsers, I decided to build an actual web page full of responsive images, which occupy a different percentage of the available width, using what I believed would be the standard ratios used in most websites. So, one image at 10% width, one at 20%, one at 25%, and so on: 33%, 50%, 66%, 75%, 100%.
For each of this images, using a front-end development framework I created with my company (The HCE Microframework), I built a version of the image at several resolutions (every 200px as well as most of the major device screen sizes on the market) and created an img tag with the appropriate srcset attribute, like this:
<div class="responsiveImage size-75"><img src="/counter.php?path=images/components/10/responsiveImage/75percent.jpg" srcset="/counter.php?path=images/components/10/responsiveImage/75percent.jpg 10w, /counter.php?path=images/components/2048/responsiveImage/75percent.jpg 2048w, /counter.php?path=images/components/1920/responsiveImage/75percent.jpg 1920w, /counter.php?path=images/components/1800/responsiveImage/75percent.jpg 1800w, /counter.php?path=images/components/1680/responsiveImage/75percent.jpg 1680w, /counter.php?path=images/components/1600/responsiveImage/75percent.jpg 1600w, /counter.php?path=images/components/1440/responsiveImage/75percent.jpg 1440w, /counter.php?path=images/components/1400/responsiveImage/75percent.jpg 1400w, /counter.php?path=images/components/1366/responsiveImage/75percent.jpg 1366w, /counter.php?path=images/components/1280/responsiveImage/75percent.jpg 1280w, /counter.php?path=images/components/1200/responsiveImage/75percent.jpg 1200w, /counter.php?path=images/components/1024/responsiveImage/75percent.jpg 1024w, /counter.php?path=images/components/1000/responsiveImage/75percent.jpg 1000w, /counter.php?path=images/components/800/responsiveImage/75percent.jpg 800w, /counter.php?path=images/components/768/responsiveImage/75percent.jpg 768w, /counter.php?path=images/components/600/responsiveImage/75percent.jpg 600w, /counter.php?path=images/components/400/responsiveImage/75percent.jpg 400w, /counter.php?path=images/components/200/responsiveImage/75percent.jpg 200w" sizes="75vw" /></div>
As you can see, the HTML gets pretty big quite quickly. This is just one image, mind. The number in the URL of the image indicates the actual image’s width.
Here’s how the page looks like:
Then we used Browserstack’s nice Automate suite to run a series of automated tests: I chose a wide range of devices and browsers supporting responsive images, and had them visit the page. We also had to run a few tests manually using an emulator simulating small smartphones, since Browserstack’s suite tends to include small smartphones with relatively obsolete browsers (their suite is meant for testing apps and website on a wide range of browsers, so this is not a criticism), and obsolete browsers have no support of responsive images.
While the browser were visiting the page, we tracked how many times each version of the same image was actually downloaded. This way, for every use of each image (100%, 50%), I hoped to have a clearer idea of what sizes might be required in an actual real-world situation.
The following graph shows the actual image sizes requested for every image in the page.
As you can see, every image has been requested at almost every size available below its maximum size on the maximum screen used. For example, the 100percent image has been requested at every size from 2048px of width downwards. 2048px width was the maximum size available for the image, and was requested by several different devices.
Let’s see a quantitative distribution for the 100% width image:
And for a 50% image:
As you can see, for every image you end up getting requests at any size available, since the combination of viewport sizes and image width produces requests for all sizes of images available.
The result is that, to accommodate every browser with the best possible image size, you should have a dedicated server providing custom images based on the browser’s request. This obviously requires big resources that are not available to everybody, even on major projects. If you have them available, the obvious solution is to output image sources for every 100px or so and let the server do the rest.
What if you don’t have a dedicated image server?
If you’re faced with a finite number of server resources it means that you have to compile images via a deploy procedure, using some automation tool like Gulp or Webpack, or you have to resize them once and for all via a CMS - and creating many different versions of the same image can easily take a long time . If you have to manage a high traffic website and you need to optimize responsive images to reduce bandwidth costs and improve user experience, you probably need to follow another approach.
“You just need three image versions” (??)
I’ve heard this sentence a lot. People assume that once you’ve done a version for mobile, desktop and tablet you are done. This is completely incorrect in my opinion. There’s a huge variety of screen resolutions on the Web, and even though current browsers are getting very good at scaling images up, interpolating pixels if the image is too small, this strategy has a severe limit. The fact is that High-DPI screens are getting common even for desktops. In my opinion, for an image that takes 100% width of your design, if you target desktops in your project, you need (at least) to output an image 1920px of width. If you then produce two other versions, you’re left with one for smaller screens at, say, 1024px or 1366px width, and a smaller one for smartphones (at 750px for an average mobile phone at 2x resolution).
But this strategy has serious drawbacks in several situations: just as an example, let’s say someone has a desktop/laptop computer with a 1440px screen resolution with a bad connection: browsers usually pick from your srcset the image that matches their on-screen requirements, or a larger image. This means that they will download an image that is much larger than required. A 1920x1080px image (16:9 ratio) can be 33% bigger than the matching 1440x810px image. This means a much slower performance for the user and much higher (unnecessary) bandwidth costs, especially if you have many images on the site.
A pragmatic solution
In my opinion, you need 6 or 7 different image versions for each image. For an image that takes 100vw on your final site, the target widths could be the following:
1920px (this covers FullHD screens and up)
1600px (this will cover 1600px desktops and several tablets in portrait mode, for example iPads at 768px width, which will request a 2x image of 1536px and above)
1366px (it is the most widespread desktop resolution)
1024px (1024x768 screens, excluding iPads which are hi-density anyway, are rarer, but I think you need some image size in between, not to leave too big a gap between pixel sizes, in case the market changes)
768px (useful for 2x 375px mobile screens, as well as any device that actually requests something close to 768px)
640px (for smartphones)
This leaves you covered for most cases. If you think that 640px is too high a minimum, keep in mind that most smartphones on the market now have high pixel density, and even though they declare 320px of viewport width, they will support double that at minimum, therefore 640px.
The above sizes are meant as a reference for 100vw images. If an image covers less screen width in your design, you need to divide the above numbers by the actual (approximate) width of the image on screen. So, for a 50vw image, you need to halve the above widths. For an image designed to occupy 33vw of the screen, you divide them by three, and so on.
Of course you can be more granular if you like. If your application/website caters mostly to mobile browser, it won’t hurt if you put more resolutions there in the lower range of sizes.
I tend to make sure to cover desktops reasonably well, since at big image sizes there are much bigger differences in size between images: the same JPG at 1920x1080px is much bigger than at 1600x900px. Furthermore, the most “vulnerable” type of device is desktops/laptops on a bad connection (imaging someone with a laptop on Wi-Fi or 4G). If you, for example, do not serve an image at 1600px, they might all request a 1920px image and the site will be much slower. Keep in mind, also, that while your site might have 60%+ of mobile users, it will still be checked and tested most of the time with desktops and laptops by most of your clients.
As usual with all these solutions, your mileage may vary, and you should carry out your own analysis based on your site’s target and visitor statistics (if they are available to you).
In case you’re wondering, srcset is supported by all major browsers, with the major exceptions of IE11 and versions of Edge below 16 (released on October 2017), check here: http://caniuse.com/#feat=srcset. For the lucky ones who can boast about not having to support IE11/Edge, please keep it to yourselves and do not brag in the comments.
Old browsers — polyfill and default image
Otherwise, since you can serve only one image to all browsers that do not support responsive images (in the normal src attribute), you need to decide whether to cater for desktop browsers, by serving a larger image, or mobile browsers, by serving a smaller image. The decision depends on your target audience and your priorities. Usually it’s safe to assume that mobile browsers will support responsive images, so you should put a version for IE11 desktop, say at 1600px of width to stay on the safe side.
Managing responsive images is not an exact science, and unless you have a lot of resources at hand, it can be a time-consuming endeavour. You must make your own choices based on your project’s requirements. I hope this article helps you in doing so. If you have something to add or some corrections to suggest, or you totally disagree on every point made above, please let me know in the comments.
(update from 2018–04–16) I’ve published a Vue.js component plugin that applies the logic above to create responsive image tags for you. Currently, it requires a server that automatically resizes images when a URL with width and height of the image is called. It’s called Vue Responsive Image. It creates src, srcset attributes, and if required also separate source tags for mobile and tablet portrait.
Some useful references
- About screen resolutions and their market share.
- Another site with useful stats.
- This article explains the basics of srcset: it is fairly old, but it’s still very good.
- The Picturefill polyfill.
- If you want to explore these issues more in depth, here’s a brilliant article by Jason Grigsby. My article approaches things from a slightly different angle, his article discusses more or less everything else you need to know about the topic.
If you have questions, or would like to let me know your thoughts on this article, feel free to do so in the comments. If you liked this article, go ahead and clap (there’s a clap button underneath the article in case you’re not familiar with Medium), and feel free to share it.