skip to content

srcset and sizes

How one img tag serves a different file per screen width and pixel density. The subtlety interviewers look for is that the browser picks a candidate before your CSS is applied, so an inaccurate sizes value silently downloads the wrong image.

part ofHTMLoverview, primer and where to startread it →
on this pageshow

questions

5

On an HTML <img>, what do the srcset and sizes attributes do, and what is the difference between a w descriptor and an x descriptor inside srcset?

level: juniorimportance: must knowfreq 72%

answer

  1. one image, several encodings
  2. descriptor describes the file
  3. display width must be declared too
  4. fixed box wants density, not width

basics

~20 s

srcset lists interchangeable files for one image: a w descriptor states a file's intrinsic pixel width, an x descriptor targets a device pixel ratio. sizes declares how wide the image will render, which the browser needs to choose among w candidates.

solid answer

~50 s

`srcset` on `<img>` gives the browser a menu of the *same* picture at different resolutions, and the browser picks one. Each candidate carries a descriptor. A **w descriptor** (`hero-800.jpg 800w`) states the file's real pixel width, and it only becomes useful when you also give `sizes` — a CSS length telling the browser how wide the image will actually be laid out, optionally with media conditions like `sizes="(min-width: 900px) 45vw, 100vw"`. From those two the browser works out how many pixels it needs and picks a candidate. An **x descriptor** (`[email protected] 2x`) targets device pixel ratio directly and is right when the rendered size is fixed — a logo or an avatar — because then `sizes` is irrelevant and is ignored. `src` stays as the fallback for anything that does not understand `srcset`. Same image content in every candidate: switching art or crop is `<picture>`'s job, not `srcset`'s.

code

html · 5 lines
html
<img src="photo-800.jpg"
     srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
     sizes="(min-width: 900px) 45vw, 100vw"
     width="1600" height="900"
     alt="Harbour at dawn">

go deeper

for a junior

Be ready to write a correct <img> with srcset and sizes from memory and to say plainly that a w descriptor is the file's own pixel width, not a breakpoint.

for a middle

Explain when x descriptors are the better tool, what the default sizes value is, and why every candidate has to be the same picture rather than a different crop.

for a senior

Show you can pick a candidate ladder that is worth building and serving, and explain what role src still plays once srcset is present.

for a principal

Own the tradeoff between generating many derivatives per image and the build, storage and cache cost of doing so, and where that decision lives in a design system.

## The problem responsive images solve A single `src` forces one file on every visitor. A 2000-pixel-wide photograph is right for a wide desktop on a high-density screen and badly wrong for a phone that will paint it 360 CSS pixels across: the phone downloads and decodes several times the bytes it can display. Serving the small file to everyone fixes the phone and leaves the desktop blurry. `srcset` exists so the author can publish several encodings of the *same* image and let the browser choose. ## The srcset syntax `srcset` is a comma-separated list of *image candidate strings*. Each is a URL plus an optional descriptor: ```html <img src="photo-800.jpg" srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w" sizes="(min-width: 900px) 45vw, 100vw" alt="…"> ``` Every candidate must be the same picture — same subject, same crop, same aspect ratio — just encoded at different sizes. Choosing a *different* crop per breakpoint is art direction and belongs to `<picture>` with `<source media>`, not here. ## w descriptors: the file's intrinsic width `photo-800.jpg 800w` says: this file is 800 image pixels wide. It is a statement about the file, not about the viewport and not about a breakpoint. This is the single most common misreading — people write `800w` believing it means "use this below 800px of viewport". It does not; the browser derives that decision itself. To derive it, the browser needs one more fact: how wide the image will be *displayed*. That is what `sizes` supplies. ## sizes: the display width, in CSS pixels `sizes` is a comma-separated list of `<media-condition> <length>` pairs, with a final bare length as the fallback. The browser evaluates the conditions in order and uses the length from the **first** match: ```html sizes="(min-width: 1200px) 600px, (min-width: 640px) 50vw, 100vw" ``` Lengths may be `px`, `em`, `rem`, `vw`, or `calc()` — but **not percentages**, because a percentage would need a containing block the browser has not laid out yet. If `sizes` is omitted while w descriptors are present, the browser assumes `100vw`, which is why full-bleed images accidentally look fine and constrained ones silently over-download. ## x descriptors: density switching When an image's rendered size is fixed by design — an avatar always 48 CSS pixels, a logo always 120 — you do not need the whole `sizes` machinery. You need one file per screen density: ```html <img src="avatar.png" srcset="avatar.png 1x, [email protected] 2x, [email protected] 3x" width="48" height="48" alt=""> ``` With x descriptors, `sizes` has no effect and is ignored. A candidate with no descriptor at all is treated as `1x`. Do not mix the two descriptor kinds in one `srcset`; the HTML standard treats a list that combines width and pixel-density descriptors as an error, so the safe rule is one descriptor type per attribute. ## What src is for once srcset exists `src` remains the fallback for anything that does not implement `srcset`. Beyond that its role depends on the descriptors: when every candidate is an x descriptor and none is `1x`, `src` participates as the `1x` candidate; when any candidate carries a w descriptor, `src` takes no part in the selection and is purely the fallback. Keep pointing it at a sensible mid-size file rather than the largest one. ## Which one should you reach for Ask whether the image's layout width varies with the viewport. - **It varies** (hero, article figure, card in a fluid grid) → w descriptors plus an honest `sizes`. - **It is fixed** (icon, logo, fixed-size avatar) → x descriptors, plus `width`/`height` attributes so the box is known. ## The mistakes that ship - Reading `800w` as a breakpoint and building a fake media-query ladder out of it. - Omitting `sizes` on a constrained image and inheriting the `100vw` default. - Putting a different crop in each candidate — the browser may swap between them at any moment, so the composition changes unpredictably. - Offering candidates that barely differ (`900w`, `950w`, `1000w`): more files to build and cache for no visible gain. - Forgetting `alt`; the responsive plumbing does nothing for accessibility.

  • If srcset lets the browser choose, why would you ever still use x descriptors?
    Because when the rendered box is fixed by design — a 48px avatar, a 120px logo — there is nothing for the browser to compute. `sizes` would just restate a constant, and x descriptors say the only thing that varies: screen density. They are shorter, they cannot go stale when the CSS changes, and they express the intent directly.
  • Can you put different crops of the picture in the same srcset to get a tighter mobile composition?
    No. Every candidate in one `srcset` must be the same image content; the browser may switch between them at will, including after load, so a differing crop would change the composition unpredictably. Art direction — a different crop or a different subject per breakpoint — is what `<picture>` with `<source media>` is for.
  • What happens to a browser that does not support srcset at all?
    It ignores `srcset` and `sizes` as unknown attributes and loads `src`, which is why `src` should point at a sensible middle-sized file rather than the largest candidate. Support is effectively universal in current browsers, so `src` is a safety net rather than a real strategy.

srcset is a menu of the same dish in different portion sizes; sizes is you telling the waiter how big the plate is. Without the plate size, the kitchen guesses the largest one.

saying these in an interview costs you the question

  • Thinks 800w means "use below an 800px viewport"
  • Puts different crops or subjects in one srcset
  • Mixes w and x descriptors in the same srcset
  • Omits sizes and assumes the browser measures the element
  • Points src at the largest file as the fallback

context

open as a page

Why does an <img> with a w-descriptor srcset need a sizes attribute at all, rather than the browser simply measuring how wide the element ends up being?

level: middleimportance: must knowfreq 62%

basics

~10 s

sizes declares the image's rendered width in CSS pixels. The browser needs it because it picks a candidate while parsing the markup, before stylesheets and layout have run; with sizes missing it assumes 100vw.

open as a page

Given <img srcset="p-400.jpg 400w, p-800.jpg 800w, p-1600.jpg 1600w" sizes="400px">, how does a browser combine sizes with the device pixel ratio to choose a candidate, and can you rely on it always picking the same one?

level: middleimportance: should knowfreq 48%

basics

~20 s

The browser resolves sizes to CSS pixels and multiplies by the screen's device pixel ratio to get the image pixels it needs — 400px at 2x asks for about 800 — then takes a candidate that meets it. Selection is a hint, so the exact file is not guaranteed.

open as a page

A product page ships images as <img srcset="…400w … 2000w" sizes="100vw">, but CSS renders each one inside a 480px-wide column on desktop. What is going wrong, how would you confirm which candidate the browser actually chose, and how do you fix sizes so it does not drift again?

level: seniorimportance: should knowfreq 44%

basics

~20 s

The sizes value promises a full-viewport image, so the browser requests a candidate sized for the whole screen instead of the 480px column — often four times the needed bytes. Confirm via the element's currentSrc and the network panel, then rewrite sizes to match the real column width.

open as a page

Across a large site, sizes values on <img> elements drift out of sync with the CSS that actually lays those images out. How would you keep responsive-image markup honest at that scale, and when would you decide an image should not carry a srcset at all?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Treat sizes as duplicated layout knowledge: generate it from the same breakpoint and container tokens the CSS uses, own it per layout slot in a component rather than per image, and check it automatically. Skip srcset where the rendered box is fixed or the image is tiny.

open as a page