skip to content

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%

answer

  1. the fetch starts before layout
  2. a promise, not a measurement
  3. no containing block to be a percentage of
  4. silent default when the attribute is absent

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.

solid answer

~50 s

By the time the element's real width is known, the download has usually already started. Browsers scan the incoming HTML ahead of the parser and begin fetching images immediately, long before the stylesheets that size that image have been fetched, parsed and applied. So the browser cannot measure — you have to *promise* a width, and `sizes` is that promise, expressed as CSS lengths guarded by media conditions that depend only on the viewport: `sizes="(min-width: 900px) 45vw, 100vw"`. The first matching condition wins, and the trailing bare length is the fallback. Percentages are not allowed, precisely because there is no laid-out containing block to be a percentage of. If you omit `sizes` while using w descriptors, the browser assumes `100vw` — a full-viewport image — which over-fetches for anything constrained by a column or a max-width. The consequence is that `sizes` duplicates a fact that lives in your CSS, and the two can silently drift apart.

code

html · 5 lines
html
<img src="card-600.jpg"
     srcset="card-300.jpg 300w, card-600.jpg 600w, card-1200.jpg 1200w"
     sizes="(min-width: 1200px) 280px, (min-width: 640px) calc(50vw - 2rem), 100vw"
     width="1200" height="800"
     alt="Grey tabby asleep on a windowsill">

go deeper

for a junior

Recall that sizes states how wide the image will be drawn and that leaving it out makes the browser assume the full viewport width.

for a middle

Explain the timing: the preload scanner requests the image before CSS and layout, so sizes is a declaration rather than a measurement, and derive the percentage ban from that.

for a senior

Demonstrate that you treat sizes as duplicated layout knowledge — show how you verify the chosen candidate against the real rendered width and how you catch drift after a redesign.

for a principal

Own the question of where this duplicated fact should live: generated from layout tokens, emitted by a component, or accepted as hand-written with a review discipline behind it.

## The timing problem The interesting part of `srcset` is not the syntax, it is *when* the decision happens. When a browser receives HTML it does two things at once. The parser builds the DOM, and a lightweight **preload scanner** runs ahead of it looking for things worth fetching now — scripts, stylesheets, images. Image requests therefore start almost as soon as the `<img>` tag's bytes arrive. At that instant: - the stylesheet that sizes the image may still be in flight; - no box has been laid out; - the containing column's width is unknown. Waiting for layout before requesting the image would mean serialising the whole critical path behind CSS, which is exactly what the preload scanner exists to avoid. So the browser asks the author instead. `sizes` is the author's declaration of how wide this image is going to be. ## What sizes may contain, and why ```html sizes="(min-width: 1200px) 600px, (min-width: 640px) calc(50vw - 2rem), 100vw" ``` Each entry is a media condition plus a length; the last entry is a bare length used when nothing else matched. Evaluation is **first match wins**, so order the conditions from most specific to least, and always end with an unconditional fallback. The permitted lengths are absolute or viewport-relative units — `px`, `em`, `rem`, `vw`, `calc()` combinations of them. **Percentages are not allowed.** That restriction follows directly from the timing: a percentage means "of the containing block", and there is no laid-out containing block yet. Everything `sizes` can express is computable from the viewport and font sizes alone. Media conditions here are also viewport-scoped for the same reason: `(min-width: 900px)` refers to the viewport, never to the element's own box. ## The default when you omit sizes Omitting `sizes` while using w descriptors does not disable selection; the browser falls back to a source size of `100vw`. That default is right for a full-bleed hero and wrong for almost everything else. A thumbnail in a card grid rendered 280 CSS pixels wide, with `sizes` omitted on a 1440-pixel viewport, will be treated as if it needed 1440 pixels — and on a 2x screen, 2880. The image looks perfect and costs an order of magnitude more bytes than it should. Nothing warns you. ## Why this duplicates your CSS, and what to do about it The uncomfortable truth of `sizes` is that it restates, in a second syntax, a layout fact that already lives in your stylesheet. Change the grid from three columns to four, or add a max-width to the article body, and the `sizes` attribute is now lying. Nothing breaks visually — the browser simply requests the wrong candidate forever. Practical mitigations: - Derive `sizes` from the same tokens the layout uses (breakpoints, gutters, container max-width) rather than eyeballing it. - Emit it from a component, so one image partial owns the value for every instance of that layout slot. - Keep it slightly conservative rather than optimistic: a `sizes` that under-states the width yields a visibly soft image, which is worse than a mild over-fetch. ## Verifying it The value that matters is the *effective* one after media conditions are evaluated at the current viewport. Two checks: ```js // the URL the browser actually settled on document.querySelector('img').currentSrc; ``` and the network panel, which shows which candidate was requested and how many bytes it cost. Compare the requested file's width against the element's real rendered width times the device pixel ratio; a large gap means `sizes` is wrong. ## Where the model is changing The HTML standard has since added `sizes="auto"`, valid only on images that are also lazily loaded, which lets the browser use the element's real layout width instead of a promise — the lazy case being precisely the one where layout is already known by the time the fetch starts. Support is not yet universal, and the syntax is designed so that `auto` comes first with ordinary sizes entries after it as the fallback for browsers that do not implement it. For eagerly loaded images above the fold, the promise model still applies. ## The takeaway for an interview Say the timing sentence out loud: *the browser chooses a candidate before it knows the element's width, so `sizes` is a promise, not a measurement.* Everything else — the ban on percentages, the viewport-only media conditions, the `100vw` default, the drift risk — falls out of that one fact.

  • Why are percentages forbidden in sizes when they are perfectly ordinary in CSS?
    A percentage is relative to the containing block, and at selection time no containing block has been laid out — that is the whole reason `sizes` exists. Every permitted unit (`px`, `em`, `rem`, `vw`, `calc()` over them) can be resolved from the viewport and font size alone, which is all the browser has when the preload scanner fires the request.
  • Is there any way to let the browser use the element's real width instead of a declared one?
    Yes, for lazily loaded images: the HTML standard added `sizes="auto"`, valid only when `loading="lazy"` is also present, because in that case layout is already known when the fetch begins. It is written first in the attribute with ordinary sizes entries after it as a fallback, since not every browser implements it yet.
  • If sizes is a promise that can drift from the CSS, is it safer to over-state or under-state it?
    Slightly over-state. An under-stated `sizes` makes the browser pick a candidate with too few pixels, and the user sees a soft, upscaled image — a visible defect on every viewport that hits it. An over-stated one costs extra bytes but looks correct, so it degrades quietly and is the safer direction to err in.

saying these in an interview costs you the question

  • Says the browser measures the element and then chooses
  • Uses a percentage or an element-relative length in sizes
  • Believes omitting sizes disables srcset selection
  • Thinks sizes media conditions refer to the image's own box
  • Writes sizes once and never revisits it when the layout changes

context