Responsive images
srcset and sizes let the browser fetch the smallest sharp source; modern formats and reserved dimensions cut bytes and layout shift for better Core Web Vitals.
In one line: ship several sizes of an image and a sizes hint, and the browser fetches the smallest one that still looks sharp — in a modern format, in a box whose dimensions are reserved so the page never jumps.
What it is
Images are usually the heaviest thing on a page, and the easiest to get wrong: a single 1200px JPEG sent to a 360px phone wastes most of its bytes, and an image with no declared size shoves the layout down when it finally loads.
Responsive images fix both. srcset offers the browser a set of sources at different widths; sizes tells it how wide the image will render; and the browser picks the smallest source that's still crisp at the device's resolution. Pair that with a modern format (AVIF, then WebP, then a JPEG fallback) and reserved dimensions, and you cut bytes dramatically while keeping the layout stable.
Why it matters
This is among the highest-leverage performance work you can do, and it maps straight onto Core Web Vitals. The hero image is usually the Largest Contentful Paint element, so shrinking it and prioritising its fetch directly improves LCP. Declaring width/height (or aspect-ratio) reserves the box before load, which is how you avoid Cumulative Layout Shift — the jarring jump when an image pops in and pushes everything down.
AVIF and WebP routinely cut file size 30–50% over JPEG at equal quality, so the format choice alone can halve your image payload.
See it
Tweak it2
Scrub the viewport width and watch which srcset candidate the browser would choose — narrower screens pull a smaller file. Switch the format to see the source name change. The box keeps its 16:9 ratio the whole time, reserved by aspect-ratio.
How it works
The browser does the selection, given good hints:
srcsetwith width descriptors —hero-400.avif 400w, hero-800.avif 800w, hero-1200.avif 1200wdeclares each source and its intrinsic width.sizes— tells the browser the image's rendered width at different breakpoints (sizes="(min-width: 60rem) 50vw, 100vw") so it can choose before layout. Get this wrong and it over- or under-fetches.<picture>+<source type>— offer AVIF first, WebP next, and a plain<img>JPEG as the universal fallback; the browser takes the first type it supports.- Reserved box — set
width/heightattributes (or CSSaspect-ratio) so space is held before the bytes arrive — no CLS. - Priority hints —
fetchpriority="high"on the LCP image,loading="lazy"on below-the-fold ones.
Build it
<picture>
<source
type="image/avif"
srcset="hero-400.avif 400w, hero-800.avif 800w, hero-1200.avif 1200w"
sizes="(min-width: 60rem) 50vw, 100vw" />
<source
type="image/webp"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
sizes="(min-width: 60rem) 50vw, 100vw" />
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1200.jpg 1200w"
sizes="(min-width: 60rem) 50vw, 100vw"
width="1200" height="675"
fetchpriority="high"
alt="Quarterly sales dashboard showing an upward trend" />
</picture>Make it yours
Use the controls beside the demo above to change viewport width and format — each change updates the example live.
Experiment in the playground- Drag the viewport down to mobile widths — the browser drops to the 400w source, sending a fraction of the bytes.
- Switch the format to JPEG and back: same markup shape, but AVIF is typically half the size for the same picture.
- The preview box never resizes as the source changes — that fixed
aspect-ratiois exactly what keeps CLS at zero.
Reproduce it with an LLM
Reproduce it with an LLM
You are a senior front-end engineer focused on Core Web Vitals. Produce a responsive `<img>` for a hero image: a `srcset` of width descriptors (400w/800w/1200w), a `sizes` attribute that matches the layout, AVIF with a WebP and JPEG fallback via `<picture>` and `<source type>`, explicit `width`/`height` (or `aspect-ratio`) to reserve the box and prevent layout shift (CLS), meaningful `alt` text, and `fetchpriority="high"` on the LCP image while lazy-loading below-the-fold ones. Briefly explain how the browser uses `srcset`/`sizes` to pick a source. Return only the HTML (and any small CSS).
Pitfalls & accessibility
Related
Core Web Vitals
Core Web Vitals score real user experience: LCP for loading, INP for responsiveness, CLS for stability. Learn the thresholds and what moves each one.
The rendering pipeline
The browser turns DOM and CSS into pixels in stages: style, layout, paint, composite. Knowing which a property triggers is the key to smooth, jank-free UI.