Responsive and performant images in practice

srcset and sizes, art direction with picture, modern formats, loading hints and preventing layout shift.

srcset and sizes

One image file cannot serve both a 360px phone and a 4K monitor well: it is either blurry or wasteful. srcset offers the browser a list of candidates, and sizes tells it how wide the image will actually be laid out, so it can pick the cheapest candidate that still looks sharp.

<img
  src="/img/hero-800.jpg"
  srcset="/img/hero-400.jpg 400w,
          /img/hero-800.jpg 800w,
          /img/hero-1600.jpg 1600w"
  sizes="(min-width: 900px) 800px, 100vw"
  alt="Team reviewing a wireframe on a whiteboard"
  width="800" height="450">
AttributePurposeGotcha
srcFallback for browsers without srcset supportStill required, and still the only source some crawlers read
srcset with wCandidate files with their intrinsic widthsMeaningless without sizes
srcset with xSwitch by device pixel ratioNever mix w and x in one attribute
sizesThe rendered width at each breakpointMust describe layout, not the file size
width/heightIntrinsic aspect ratioInclude both, or the page shifts as the image loads
⚠️
If you use w descriptors and omit sizes, the browser assumes the image spans the full viewport and downloads a candidate far larger than needed. That one missing attribute can triple image bytes on a desktop.

Art direction with picture

<picture> exists for the cases srcset cannot express: serving a newer file format, or showing a different crop at different widths rather than the same picture at different sizes.

<picture>
  <source type="image/avif" srcset="/img/hero.avif 1x, /img/[email protected] 2x">
  <source type="image/webp" srcset="/img/hero.webp 1x, /img/[email protected] 2x">
  <img src="/img/hero.jpg" alt="Sunlit meeting room" width="800" height="500">
</picture>

<picture>
  <source media="(min-width: 900px)" srcset="/img/portrait-wide.jpg">
  <img src="/img/portrait-square.jpg" alt="Portrait of the author" width="400" height="400">
</picture>
  • The browser walks the <source> list in order and takes the first one it can decode.
  • A type attribute lets format negotiation happen without JavaScript and without a polyfill.
  • A media attribute handles art direction — a wide crop for desktop, a square crop for phones.
  • The final <img> is mandatory inside <picture>: it carries the alt, the dimensions and the fallback.

Loading hints and layout shift

Once the right file is chosen, when and how it loads still matters. Lazy loading below-the-fold images removes them from the critical path; forcing a hero image to load early stops it from arriving after the text.

<img src="/img/article-1.jpg" alt="A laptop on a cluttered desk"
     width="1200" height="675" loading="lazy" decoding="async">

<img src="/img/hero.jpg" alt="Gradient banner behind the product name"
     width="1600" height="900" fetchpriority="high" decoding="async">
  • Reserve space with width and height, or with aspect-ratio in CSS: the browser then knows the box before the bytes arrive.
  • Keep above-the-fold images eager. Lazy-loading a hero image delays the largest paint on the page.
  • Use decoding="async" on large images so decoding stays off the main thread.
  • Save at a sensible quality and check the format — AVIF or WebP typically beats JPEG by a wide margin at the same perceived sharpness.
  • Do not lazy-load a small inline logo; the request overhead exceeds the saving.

FAQ

My hero image got slower after I added loading="lazy". Why?
Lazy loading defers everything below a threshold distance from the viewport. For the first image on the page, keep it eager and add fetchpriority="high" instead.
Do I still need srcset if I use picture?
Yes. They solve different problems and combine well: <source> chooses the format or crop, srcset inside it chooses the resolution.

Inline SVG and icon markup The document head: metadata, favicons and social cards

Last refreshed 2026-09-18.