srcset: Multiple Image Sources
The srcset attribute provides a list of image sources with their intrinsic widths. The browser chooses the best one based on the current viewport width and device pixel ratio.
<!-- srcset with width descriptors -->
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w,
hero-2400.jpg 2400w"
alt="Hero image"
width="800"
height="600"
>
<!--
Without the sizes attribute, the browser assumes the image
is 100vw wide — it picks hero-2400.jpg for a 1200px Retina
display (1200px × 2x = 2400w). This is technically correct
but wastes bandwidth if the image is displayed smaller.
Add sizes to give the browser context about display size.
-->
sizes: Tell the Browser the Display Size
The sizes attribute describes how wide the image will actually be displayed. The browser uses this — combined with viewport width — to select the most appropriate srcset source.
<!-- Full-width image on mobile, 50% width on desktop -->
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w,
photo-800.jpg 800w,
photo-1200.jpg 1200w,
photo-1600.jpg 1600w"
sizes="(max-width: 768px) 100vw,
(max-width: 1200px) 50vw,
800px"
alt="Photo description"
width="800"
height="600"
loading="lazy"
>
<!--
How the browser uses this:
- On a 375px mobile screen: sizes → 100vw = 375px, device 2x → needs 750px
→ picks photo-800.jpg ✓
- On a 1440px desktop: sizes → 800px, device 1x → needs 800px
→ picks photo-800.jpg ✓
- On a 1440px Retina: sizes → 800px, device 2x → needs 1600px
→ picks photo-1600.jpg ✓
-->
picture: Art Direction & Format Switching
The picture element wraps multiple source elements, each with srcset. The browser uses the first matching source. Use it for art direction (different crops per breakpoint) or format switching (AVIF → WebP → JPEG).
<!-- Art direction: different crop ratios per breakpoint -->
<picture>
<!-- Portrait crop for mobile -->
<source
media="(max-width: 640px)"
srcset="hero-portrait-640.webp 640w, hero-portrait-1280.webp 1280w"
type="image/webp">
<source
media="(max-width: 640px)"
srcset="hero-portrait-640.jpg 640w, hero-portrait-1280.jpg 1280w">
<!-- Landscape crop for desktop -->
<source
srcset="hero-landscape-800.webp 800w, hero-landscape-1600.webp 1600w"
type="image/webp">
<!-- Fallback img (required) -->
<img src="hero-landscape-800.jpg" alt="Hero" width="1600" height="900">
</picture>
<!-- Format switching: AVIF → WebP → JPEG -->
<picture>
<source srcset="photo.avif" type="image/avif">
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="Photo" loading="lazy">
</picture>
Device Pixel Ratio Descriptors
For fixed-size images (like icons, avatars, product thumbnails), use the x descriptor instead of w+sizes. It's simpler when the display size is always the same.
<!-- Avatar always displays at 64×64, but serve 128px for Retina -->
<img
src="avatar-64.png"
srcset="avatar-64.png 1x, avatar-128.png 2x, avatar-192.png 3x"
alt="User avatar"
width="64"
height="64"
>
<!-- Retina logo in header (fixed 200px wide) -->
<img
src="logo.png"
srcset="logo.png 1x, logo@2x.png 2x"
alt="Company logo"
width="200"
height="50"
>
Common Responsive Image Mistakes
- Omitting the fallback src — the src attribute on img is required as fallback for browsers that don't support srcset (rare in 2026, but also used as the default in picture elements).
- Not specifying width and height — missing dimensions cause CLS (Cumulative Layout Shift) as the page reflows when the image loads. Always set width/height equal to the intrinsic dimensions of the source image.
- Lazy-loading hero/LCP images — hero images (the Largest Contentful Paint element) should never have loading='lazy'. Lazy loading delays them, worsening LCP. Only lazy-load below-the-fold images.
- Generating too many srcset sizes — you don't need an image for every 50px increment. 3–4 breakpoint sizes (e.g., 400, 800, 1200, 2400) cover most cases efficiently.
- Using vw in sizes without calc for padding — if your container has 16px padding on each side, the image isn't truly 100vw — it's calc(100vw - 32px). This matters for accurate browser selection.
Frequently Asked Questions
- Does the browser always pick the smallest image in srcset?
- No — the browser picks the smallest source that meets the quality requirements for the current display size and pixel density. It may also factor in connection speed on mobile (it can pick a smaller image on slow connections even if a larger one would technically fit).
- Should I use srcset or picture?
- Use srcset+sizes for most responsive images (same content, different sizes). Use picture when you need art direction (different crops) or format fallbacks (AVIF/WebP/JPEG). For format fallbacks specifically, picture is the only way.
- What is preload and when should I use it for images?
- Use
<link rel="preload" as="image" href="…">for your LCP hero image — this tells the browser to fetch it before it parses the img tag in the body. For responsive images, use imagesrcset and imagesizes attributes on the preload link to preload the correct breakpoint variant. - How many srcset sizes should I generate?
- For most images: 400w, 800w, 1200w, and 2400w. This covers mobile (400), tablet (800), desktop 1x (1200), and desktop 2x Retina (2400). For full-bleed hero images, add 3200w for very large screens.