The problem: one image for all devices
A single <img src="..."> serves everyone the same image — the 4K desktop and the old phone alike. Either it's too large for the phone (wasted bandwidth, slow load) or too small for the desktop (blurry). Responsive images solve this: the browser is offered several variants and picks the right one itself.
srcset: offering the choice
With srcset you list image variants along with their real pixel width (the w stands for width in pixels):
<img
src="photo-800.jpg"
srcset="photo-480.jpg 480w,
photo-800.jpg 800w,
photo-1200.jpg 1200w,
photo-1600.jpg 1600w"
alt="Description">The src remains as a fallback for very old browsers. Everyone else reads srcset and now knows: this image exists in four widths. What's still missing is how wide the image appears in the layout — that's what sizes is for.
sizes: telling the browser the layout width
Without sizes, the browser assumes the image fills the full viewport width. If that's not true (say, in a two-column layout), it loads too large. sizes describes the display width per screen size:
<img
src="photo-800.jpg"
srcset="photo-480.jpg 480w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw,
(max-width: 1000px) 50vw,
800px"
alt="Description">Read it as: "up to 600px viewport width the image fills 100% of the width; up to 1000px it fills half; otherwise it's 800px wide." From that width plus the device's pixel density, the browser computes which srcset variant fits best — and loads only that one.
Why this also serves Retina displays
The clever part: a Retina phone with 400 CSS pixels of width and double pixel density needs an 800px-wide image for a crisp result. The browser works exactly that out from sizes and device density — you don't have to manage @2x variants yourself.
Creating the variants
For each srcset width you need a real image file. You produce them from the original by scaling it down to the respective widths — with the resize tool (480, 800, 1200, 1600 px) and then compressing. Practical rule of thumb for the widths:
- 480w — smartphones
- 800w — tablets, small layouts
- 1200w — desktop
- 1600w — large/Retina displays
Take format selection along too: the picture element
If you also want to serve modern formats (WebP, AVIF) with a fallback, combine srcset with the <picture> element:
<picture>
<source type="image/avif" srcset="photo.avif">
<source type="image/webp" srcset="photo.webp">
<img src="photo.jpg" alt="Description" width="800" height="600">
</picture>The browser takes the first format it understands. Modern browsers get small AVIF/WebP files, old ones get the JPG fallback — all without JavaScript.