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">| Attribute | Purpose | Gotcha |
|---|---|---|
src | Fallback for browsers without srcset support | Still required, and still the only source some crawlers read |
srcset with w | Candidate files with their intrinsic widths | Meaningless without sizes |
srcset with x | Switch by device pixel ratio | Never mix w and x in one attribute |
sizes | The rendered width at each breakpoint | Must describe layout, not the file size |
width/height | Intrinsic aspect ratio | Include both, or the page shifts as the image loads |
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
typeattribute lets format negotiation happen without JavaScript and without a polyfill. - A
mediaattribute handles art direction — a wide crop for desktop, a square crop for phones. - The final
<img>is mandatory inside<picture>: it carries thealt, 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
widthandheight, or withaspect-ratioin 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?
fetchpriority="high" instead.Do I still need srcset if I use picture?
<source> chooses the format or crop, srcset inside it chooses the resolution.Related
Inline SVG and icon markup The document head: metadata, favicons and social cards
Last refreshed 2026-09-18.