Responsive images with srcset and sizes
srcset and sizes let each device download only the pixels it needs. Configured properly, they cut mobile image weight to a fraction.
The problem they solve
The same image displays 360 px wide on a phone, 768 px on a tablet and 1400 px on a laptop, on screens with 1x, 2x or 3x pixel density. If you serve a single file, it's either too big for phones (wasting data and hurting LCP) or too small for desktops (looking blurry).
With srcset you describe which files you have and with sizes how large the image will be displayed. With that information, the browser —which knows the viewport width and screen density— picks the optimal file.
srcset with width descriptors (w)
This is the most useful form and the one you should almost always use:
<img
src="photo-1280.jpg"
srcset="photo-640.jpg 640w, photo-1280.jpg 1280w, photo-1920.jpg 1920w"
sizes="(min-width: 1024px) 800px, 100vw"
width="1920" height="1080"
alt="Photo description">
Each entry is file real_width_in_pixels followed by w. The src acts as a fallback for very old browsers.
The sizes attribute
sizes states the on-screen width the image will take, not the file's width. It's a list of media conditions with a width, plus a final default value:
sizes="(min-width: 1024px) 800px, (min-width: 640px) 50vw, 100vw"
It reads: on screens at least 1024 px wide, the image takes 800 px; between 640 and 1023 px, half the viewport; below that, the full viewport.
The browser multiplies that width by the pixel density and picks the smallest file that covers it. For example, on a 390 px-wide phone at 3x it needs about 1170 px, so it'll pick photo-1280.jpg.
Common sizes mistakes
- Leaving it out. Without it, the browser assumes
100vwand on desktop downloads the largest image even if it's shown in a narrow column. - Using the file width.
sizesdescribes the layout, not the files. - Not matching the CSS. If your CSS changes, review
sizes. You can verify it in DevTools: the element'scurrentSrcproperty shows which file was chosen.
If you use loading="lazy", Chromium-based browsers already accept sizes="auto" (write sizes="auto, 100vw" as a fallback), which computes the value from the actual layout. It only works on lazy-loaded images.
srcset with density descriptors (x)
For fixed-size images —a 64 px avatar, a logo— density descriptors are enough:
<img src="avatar-64.png" srcset="avatar-64.png 1x, avatar-128.png 2x" width="64" height="64" alt="">
Don't mix w and x in the same srcset.
Which widths to generate
You don't need one variant per device. A practical rule is to cover jumps of 30–50% in file size:
| Image type | Suggested widths |
|---|---|
| Full-screen hero | 640, 960, 1280, 1920, 2560 |
| Article image (~800 px column) | 480, 800, 1200, 1600 |
| Grid card | 320, 480, 640, 960 |
In ImageZap, turn on Responsive variants, tick the widths and the code generator will write the srcset with the real file names.
srcset inside picture
If you also want to serve several formats, each <source> gets its own srcset and sizes:
<picture>
<source type="image/avif" srcset="photo-640.avif 640w, photo-1280.avif 1280w" sizes="(min-width: 1024px) 800px, 100vw">
<source type="image/webp" srcset="photo-640.webp 640w, photo-1280.webp 1280w" sizes="(min-width: 1024px) 800px, 100vw">
<img src="photo-1280.jpg" srcset="photo-640.jpg 640w, photo-1280.jpg 1280w" sizes="(min-width: 1024px) 800px, 100vw" width="1280" height="720" alt="…">
</picture>
Don't forget width and height
The width and height attributes don't set the on-screen size (CSS does that), but they let the browser reserve space with the right aspect ratio before the image arrives. That prevents layout shifts (CLS). Combine them with:
img { max-width: 100%; height: auto; }
Frequently asked questions
Why does the browser download a bigger image than expected?
Probably because of screen density (a 3x phone needs three times the pixels) or because sizes is missing. Check currentSrc in DevTools.
Does the browser re-download when the window is resized?
It may download a larger variant if needed, but it usually won't switch down to a smaller one if a larger one is already cached.
Do I need srcset if I use an image CDN?
Many CDNs generate the variants for you, but you still need to write srcset and sizes (or use a component that does) so the browser can choose.
How many variants are too many?
More than 5–6 per format rarely helps. Each extra variant means more generation and caching work for marginal savings.