Responsive Images (srcset, sizes)
A single <img> tag with one src forces every device to download the same file — a phone
on a slow connection gets the same multi-megabyte image as a 4K desktop monitor. The srcset
and sizes attributes let the browser choose the most appropriate image from a list of
candidates, based on the device's screen size and pixel density.
This is "resolution switching" — the same image content at different file sizes, not to be
confused with <picture>'s "art direction" (different crops or compositions), which is a
separate topic.
The Problem srcset Solves
<!-- Without srcset: everyone downloads the same 2400px-wide image --> <img src="hero-2400w.jpg" alt="Product hero shot">
Width Descriptors (w)
The most common form of srcset lists candidate images with a width descriptor — the
image's actual intrinsic pixel width, suffixed with w. Combined with sizes, this tells the
browser exactly how large the image will be displayed at each viewport width, so it can pick
the smallest candidate that is still big enough.
<img
src="hero-800w.jpg"
srcset="
hero-400w.jpg 400w,
hero-800w.jpg 800w,
hero-1200w.jpg 1200w,
hero-2400w.jpg 2400w
"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
alt="Product hero shot"
>Attribute | Meaning |
|---|---|
srcset | A comma-separated list of image URLs, each tagged with its real pixel width (e.g. 800w) |
sizes | A comma-separated list of media conditions describing how wide the image will be RENDERED at each breakpoint |
src | The fallback image for browsers that do not support srcset — always include it |
sizes left to right: the browser picks the first media condition that matches the viewport and uses its rendered width. The final entry (no condition) is the default for anything that did not match earlier conditions.How the Browser Picks the "Best" Image
The browser reads sizes to determine the CSS pixel width the image will render at, given the current viewport.
It multiplies that width by the screen’s device pixel ratio (DPR) — e.g. 2x on a typical retina phone.
It picks the smallest candidate from srcset whose width descriptor is greater than or equal to that target pixel value.
The choice can differ between page loads or devices — this is intentional; the spec leaves the exact algorithm up to the browser to allow for network-aware and cache-aware heuristics.
Viewport: 375px wide phone, DPR 2 --> effective target ~750px sizes says: image renders at 100vw at this width --> 375px CSS width 375 * 2 = 750px needed --> browser picks hero-800w.jpg (smallest candidate >= 750) Viewport: 1440px wide desktop, DPR 1 sizes says: at max-width 1200px doesn't match, falls to default "800px" 800 * 1 = 800px needed --> browser picks hero-800w.jpg
Pixel-Density Descriptors (x)
When the image is always displayed at a fixed CSS size regardless of viewport (a logo, an
avatar, an icon), you don't need sizes at all — use pixel-density descriptors instead. These
tell the browser directly: "this file is meant for a 1x screen, this one for a 2x screen."
<img src="avatar.jpg" srcset="avatar.jpg 1x, avatar@2x.jpg 2x, avatar@3x.jpg 3x" alt="User avatar" width="64" height="64" >
Descriptor type | Use when... | Requires sizes? |
|---|---|---|
w (width) | The rendered size of the image changes across breakpoints | Yes |
x (pixel density) | The rendered size is fixed; only the pixel density varies | No |
srcset must use either all w descriptors or all x descriptors — never both in the same attribute.A Real Breakpoint Example
A blog post's hero image that spans the full content width on mobile, but is capped at 900px inside a centered container on larger screens:
<img
src="article-hero-900w.jpg"
srcset="
article-hero-480w.jpg 480w,
article-hero-900w.jpg 900w,
article-hero-1400w.jpg 1400w,
article-hero-2000w.jpg 2000w
"
sizes="(max-width: 900px) 100vw, 900px"
alt="Team standing in front of the new office"
width="900" height="500"
loading="lazy"
>width and height attributes (or their aspect ratio via CSS) even when using srcset. The browser needs the intrinsic aspect ratio up front to reserve layout space and avoid content shifting as images load — this is a major factor in the Cumulative Layout Shift metric.Common Mistakes
Forgetting sizeswhen using width (w) descriptors — without it, the browser assumes 100vw, which often picks a much larger image than necessary.Listing srcset candidates out of order — order does not matter to the browser, but keeping them ascending makes the markup easier for humans to read and maintain.
Using only 2 candidate sizes for a widely responsive image — more steps (3–5) gives the browser finer-grained choices and saves more bandwidth on average.
Omitting the plain
srcfallback — required for the rare browser or crawler that ignores srcset entirely.
Key Takeaways
srcset lists candidate image files; the browser — not you — picks the best one at load time.
Width descriptors (w) work together with sizes to describe how large the image renders at each breakpoint.
Pixel-density descriptors (x) are simpler and fit images with a fixed display size across all viewports.
The picking algorithm considers rendered CSS size and device pixel ratio, and results can legitimately vary between browsers.
Always keep width/height attributes to prevent layout shift, regardless of which descriptor type you use.