srcset lists candidate files; sizes tells the browser the slot width in CSS pixels before layout completes. Sites that ship srcset without an honest sizes make the browser guess, and its guess is the biggest file.
<img
src="photo-800.webp" alt="the workshop bench"
srcset="photo-400.webp 400w, photo-800.webp 800w, photo-1600.webp 1600w"
sizes="(max-width: 700px) 92vw, 560px"
width="800" height="533" fetchpriority="high" />The two-minute math
- Open devtools and read the rendered image width at each breakpoint you support. That IS your sizes value; write it in px, not vw, where the slot is fixed.
- Generate files at the two ends of the slot range (1x and 2x density of the largest). Three candidates is already plenty.
- Never mix density descriptors (1x, 2x) with width descriptors (w) in one srcset; one syntax wins and it is the w form for layouts.
The bugs this guide fixes
- A desktop hero shipped at desktop bytes on phones: sizes forgot the (max-width) case.
- A fuzzy 400px photo stretched on desktop: no bigger candidate existed to upgrade to.
- Layout shift on load: missing width/height attributes. Put them back; they cost nothing.
This site: one 3:2 crop per article photo, hero-eager on article pages, everything else lazy, Lighthouse 100 across every page measured in this repo. Simple, measured, and the bytes follow the viewport.
