A fixed <img src> sends the same file to a 4K desktop and a budget phone on mobile data — too heavy for one, too small for the other. The srcset attribute fixes this by offering the browser a menu of options and letting it pick.

01srcset and sizes: letting the browser decide

List the same image at different widths with a w descriptor, and tell the browser how wide it will actually render with sizes. Armed with both — plus the device pixel ratio — the browser picks the best source itself:

<img
  src="hero-800.jpg"
  srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"
  sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
  alt="Mountain landscape at dawn">

For fixed-size UI like logos, the simpler x descriptor works instead: srcset="logo.png 1x, logo@2x.png 2x". A src fallback is mandatory — it’s what old browsers and RSS/email use.

02<picture>: format fallbacks and art direction

srcset switches between sizes; <picture> switches between formats — or serves a different crop on mobile. Stack <source> elements, most modern format first:

<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Mountain landscape at dawn" width="800" height="533">
</picture>

The <img> at the bottom is not optional — it’s the fallback and the element that carries alt, dimensions and classes. Use the media attribute on a <source> for true art direction (a portrait crop on mobile, landscape on desktop) — there the browser must honour your condition.

Always set width and height to eliminate layout shift (CLS). Lazy-load below-the-fold images, but keep the hero eager — it’s often the Largest Contentful Paint element.