srcset has been in every browser for the better part of a decade, and it is still
the most commonly misused piece of image markup on the web. Usually in one of two ways: not
used at all, or used with a continuous range of widths that defeats the caching underneath it.
What srcset is for
You give the browser a list of candidates and it chooses. It knows things your server cannot: the viewport, the device pixel ratio, and in some cases the network conditions.
<img
src="/v1/hero.jpg?w=960&f=auto"
srcset="/v1/hero.jpg?w=640&f=auto 640w,
/v1/hero.jpg?w=960&f=auto 960w,
/v1/hero.jpg?w=1280&f=auto 1280w,
/v1/hero.jpg?w=1920&f=auto 1920w"
sizes="(max-width: 700px) 100vw, 960px"
width="1920" height="1080"
alt="…">
Four things in there are doing work, and it is worth being precise about which:
The w descriptors describe the file, not the layout
640w means "this file is 640 pixels wide". It is not a media query and it is not
a breakpoint. Getting this wrong — writing the CSS width instead of the file width — makes
every subsequent calculation wrong.
sizes describes the layout, and it is the one people omit
sizes tells the browser how wide the image will be rendered, before any
CSS has been parsed. Without it, the browser assumes 100vw and will cheerfully
pick your largest candidate for an image that ends up in a 300-pixel sidebar.
This is the single most common srcset bug: a correct candidate list, no
sizes, and every visitor downloading the 1920px file anyway.
width and height stop the page jumping
They do not need to be the displayed size — the browser derives an aspect ratio from them and reserves the space. Omitting them is one of the largest contributors to layout shift, and it costs nothing to fix.
src is the fallback
Anything that does not understand srcset uses it. Pick a middle candidate rather
than the largest.
How many widths?
The instinct is "as many as possible, so the browser can pick the perfect one". This is wrong, for a reason that has nothing to do with markup: every distinct width is a distinct cache entry, a distinct origin fetch and a distinct encode. Twenty candidates spread your traffic across twenty entries, each of which warms up twenty times more slowly.
The saving from a perfectly-sized image over one that is 20% too large is small. The cost of a cache miss is not. Four or five widths is almost always the right answer.
Choosing them
- Start from the layout. What is the widest this image is ever rendered at? That is your largest candidate, doubled if you care about high-density displays.
- Work down in rough steps. Halving is a reasonable default: 1920, 960, 640, 320. The visual difference between adjacent candidates should be noticeable, or the candidate is not earning its cache entry.
- Stop when the smallest is smaller than your smallest container. Below that you are shipping fewer pixels than the layout needs.
Density descriptors, briefly
There is a second syntax, for images with a fixed CSS size — logos, avatars, icons:
<img src="/v1/logo.png?w=200"
srcset="/v1/logo.png?w=200 1x,
/v1/logo.png?w=400 2x"
width="200" height="60" alt="…">
Use x descriptors when the rendered size never changes, and w plus
sizes when it does. Mixing them in one srcset is invalid; browsers
will do something, but not necessarily what you meant.
Art direction is a different problem
srcset serves the same image at different sizes. If you need a different
crop on mobile — a wide banner that becomes a square, a subject that would be cropped
out at a narrow aspect ratio — that is art direction, and it needs
<picture> with media queries:
<picture>
<source media="(max-width: 700px)" srcset="/v1/hero.jpg?w=700&h=700&fit=cover">
<img src="/v1/hero.jpg?w=1600&h=600&fit=cover" alt="…">
</picture>
The two mechanisms answer different questions and are frequently confused.
srcset is "how big"; <picture> with media is
"which picture".
Putting it together
A workable default for most sites:
- four or five widths, chosen from the layout, written down somewhere central;
sizeson every responsive image, because the browser guesses badly without it;widthandheightalways, for the aspect ratio;f=autofor the format, so the candidate list does not triple;- quality fixed server-side rather than set per URL.
That is a handful of cache entries per image, a page that does not jump, and phones that stop downloading desktop heroes. None of it is new, and most of it is still not done.