The one-line fix
Delete your hand-written sizes media query and write auto instead. That is the whole change. The browser does the measuring your CSS used to promise.
<img src="hero-wide-640.jpg"
srcset="hero-wide-320.jpg 320w,
hero-wide-640.jpg 640w,
hero-wide-1280.jpg 1280w"
sizes="auto"
loading="lazy"
width="1280" height="549"
alt="Lighthouse on a coastal cliff at golden hour">
sizes. Without any sizes, the browser assumes 100vw and downloads for your whole viewport even when the image renders in a 300px card. A hand-written sizes="(max-width: 900px) 100vw, 900px" fixes that until the layout changes and nobody updates the markup. auto reads the real slot width, so it cannot go stale.
Watch it work
The exact same image markup in three different containers. Each readout shows the slot width, the file the browser actually chose, and what a 100vw fallback would have downloaded. Resize the window, hit re-run, and watch the files change.
sizes="auto"
sizes="auto"
sizes="auto"
This is the case hand-written sizes gets wrong most often: a narrow rail inside a wide page. Nobody writes a media query for "the sidebar", so the image quietly downloads the desktop file. auto just measures the rail.
sizes="100vw"
This is what the browser does when sizes is missing or wrong: it assumes the image spans the viewport and grabs the biggest file it needs for that. Same 280px slot as C, wildly different download.
Selection happens once per load, not on resize: the browser picks a file when it starts fetching. That is why the re-run button exists. Shrink the window, re-run, and compare A against C.
Browser support
| Browser | Support |
|---|---|
| Chrome / Edge | 126+ (June 2024) |
| Firefox | 150+ (April 2026) |
| Safari / iOS Safari | Not supported |
| Baseline | No — blocked by Safari |
Roughly 70% global coverage as of mid-2026, and under 1% of page loads actually use it. Genuinely underused.
Honest notes
- It goes on the
<img>, not on<source>. Inside<picture>, a matched<source>inherits the img'ssizeswhen it does not set its own. Do not putsizes="auto"on the source elements. - Lazy only. The spec applies it to
loading="lazy"images, so never use it on your LCP or hero image. The browser has to finish layout before it can measure the slot, which delays the single most important image on the page. Keep an explicitsizesthere. - It picks the file, not the crop. If you want a different crop per container, that is still
<picture>media queries or JavaScript (aResizeObserverswapping sources).autosolves resolution selection. - On Safari it falls back to
100vw. Oversized, but sharp, never broken. Writingsizes="auto, 100vw"makes the fallback explicit, and Shopify'sautosizespolyfill closes the gap if your Safari traffic matters. - Keep
widthandheighton the<img>. Same as always: they reserve space and prevent layout shift while the lazy image waits below the fold.
loading="lazy". Without it, sizes="auto" is invalid and the browser falls back to 100vw. If every image on the page is above the fold, you do not need lazy loading and you do not need auto either. This attribute is for below-the-fold images in real layouts.