Responsive Images — srcset, sizes, <picture>
TL;DR
Serve the right image for each device with native HTML — no JS. srcset + sizes lets the browser pick by resolution/viewport (resolution switching); <picture> with <source> handles art direction (different crops) and format fallback (AVIF/WebP → JPEG). Add width/height (or aspect-ratio) to prevent layout shift (CLS), loading="lazy" for below-the-fold, and fetchpriority="high" for the LCP hero. These are the cheapest Core Web Vitals wins — see ../15_performance/01_core_web_vitals.md.
Interview Q&A
Q: srcset with width descriptors + sizes — how does it work?
A: You list candidate images with their intrinsic widths (w), and tell the browser how wide the image will render at each breakpoint via sizes. The browser picks the smallest file that’s still sharp for the device’s viewport and DPR:
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
width="800" height="600" alt="…" />
“On screens ≤600px the image is full width; otherwise half.” The browser does the math (CSS px × DPR) and chooses. src is the fallback for old browsers.
Q: Width descriptors (w) vs density descriptors (x)?
A:
w+sizes— for images whose rendered size varies with the layout (responsive). Most cases.x— for a fixed display size, just swapping for DPR (1×/2×/3×):srcset="logo.png 1x, logo@2x.png 2x". Nosizesneeded.
Use w for fluid images, x for fixed-size assets like logos/icons.
Q: When do you use <picture> instead of srcset?
A: Two cases srcset alone can’t handle:
- Art direction — a different crop/image per breakpoint (wide banner on desktop, square on mobile), not just a smaller version.
- Format negotiation with fallback — offer AVIF/WebP and let the browser fall back to JPEG.
<picture>
<source type="image/avif" srcset="hero.avif" />
<source type="image/webp" srcset="hero.webp" />
<source media="(max-width: 600px)" srcset="hero-square.jpg" />
<img src="hero.jpg" width="1200" height="630" alt="…" />
</picture>
The browser uses the first matching <source>; <img> is the required fallback and carries alt.
Q: How do these attributes affect Core Web Vitals?
A:
width/height(or CSSaspect-ratio) reserve space → no layout shift → better CLS.loading="lazy"defers offscreen images → less bandwidth, faster initial load. Never lazy-load the LCP image.fetchpriority="high"on the hero tells the browser to load it first → better LCP.fetchpriority="low"for de-prioritizing.decoding="async"avoids blocking rendering on image decode.- Smaller, modern formats (AVIF/WebP) cut bytes → faster LCP.
Q: What’s the difference between loading="lazy" and a preload?
A: Opposite intents. loading="lazy" delays offscreen images. <link rel="preload" as="image" fetchpriority="high"> promotes a critical image (the LCP) so the preload scanner fetches it early — useful when the image is set via CSS or discovered late. See ../15_performance/.
Gotchas / edge cases
- Lazy-loading the LCP image hurts LCP — the single most common image perf mistake. Hero = eager +
fetchpriority="high". - Missing
sizeswithwdescriptors defaults to100vw, so the browser over-downloads on multi-column layouts. - No
width/height→ the image pops in and shifts content (CLS). Always set dimensions oraspect-ratio. <picture>altgoes on the<img>, not on<source>; omitting the<img>breaks everything.- CDN/
<img>srcset explosion — generating dozens of sizes is wasteful; pick a sensible ladder (e.g., 400/800/1200/1600) matched to breakpoints. Image CDNs (Cloudinary, Next<Image>) automate this. - Empty
alt=""is correct for decorative images (skip them in AT); meaningful images need descriptive alt.
What a senior is expected to say
- “
srcset+sizes(width descriptors) for resolution switching;xdescriptors for fixed-size DPR swaps;<picture>for art direction and AVIF/WebP fallback.” - “Always set
width/heightoraspect-ratioto avoid CLS; lazy-load below the fold but never the LCP image.” - “
fetchpriority='high'on the hero is the cheapest LCP win;decoding='async'avoids decode-blocking.” - “Modern formats and a sensible size ladder cut bytes — I let an image CDN/
next/imagegenerate them.”
Cross-references
- Core Web Vitals (LCP/CLS impact): ../15_performance/01_core_web_vitals.md
- Resource hints (preload/preconnect): ../15_performance/
- Decorative vs meaningful alt (a11y): ../16_accessibility/02_semantic_html_and_landmarks.md
Further reading
- MDN — Responsive images: https://developer.mozilla.org/en-US/docs/Web/HTML/Responsive_images
- MDN —
<picture>: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture - web.dev —
fetchpriority: https://web.dev/articles/fetch-priority