Responsive Images Craft
When to Use
Use
srcset+sizeson<img>when serving the same crop at different sizes. Use<picture>with<source media>when you need different crops or compositions per breakpoint (art direction). Use<picture>with<source type>for format fallback chains.
Decision
| If you need... | Use... | Why |
|---|---|---|
| Same image, different resolutions (1x/2x) | srcset with density descriptors (1x, 2x) |
Browser picks density match; no math needed |
| Same image, different widths for different viewports | srcset with width descriptors (400w, 800w) + sizes |
Browser picks best width based on sizes hint |
| Completely different crops per breakpoint | <picture> + <source media="..."> |
Forces specific image at specific breakpoint |
| Format fallback (AVIF → WebP → JPEG) | <picture> + <source type="..."> |
Browser picks first supported format |
| Art direction AND format fallback | <picture> with both type and media on <source> |
Combine both axes |
Image breakpoint strategy: Image breakpoints are not the same as CSS layout breakpoints. Choose based on file size jumps (~30% steps): 1920 → 1280 → 900 → 600 → 400. For hero images: 2560, 1920, 1280, 900, 640. For card grids: 800, 600, 400, 300.
Pattern
Resolution switching (most common):
<img
srcset="hero-640.jpg 640w, hero-1280.jpg 1280w, hero-1920.jpg 1920w"
sizes="(max-width: 640px) 100vw, (max-width: 1280px) 80vw, 1200px"
src="hero-1280.jpg"
alt="Descriptive alt text"
width="1280"
height="720"
>
Art direction (different crop per breakpoint):
<picture>
<source
media="(max-width: 640px)"
srcset="hero-mobile-640.jpg 640w, hero-mobile-1280.jpg 1280w"
sizes="100vw"
>
<source
media="(min-width: 641px)"
srcset="hero-desktop-1280.jpg 1280w, hero-desktop-1920.jpg 1920w"
sizes="(max-width: 1280px) 80vw, 1200px"
>
<img src="hero-desktop-1280.jpg" alt="..." width="1280" height="720">
</picture>
sizes calculation — describes the image's rendered width at each viewport:
- 100vw = full-width image
- (max-width: 768px) 100vw, 50vw = full-width on mobile, half on desktop
- (max-width: 640px) calc(100vw - 32px), (max-width: 1024px) 50vw, 400px = accounts for padding
sizes does not control layout — CSS controls layout. Mismatching sizes and CSS wastes bandwidth.
Common Mistakes
- Wrong: density descriptors (
2x) for layout images → Right: use width descriptors (w); density descriptors are for fixed-size UI elements only - Wrong:
sizes="100vw"on a card that's 33vw on desktop → Right: matchsizesto the actual rendered width - Wrong: omitting
widthandheightattributes → Right: always include; browser needs them to reserve space and prevent CLS - Wrong:
<picture>for resolution switching → Right:srcset+sizeson<img>is simpler;<picture>is for art direction or format fallback
See Also
- Image Format Strategy — combine
<picture>type sources with format fallbacks - Loading and Decode Craft — add
loading,fetchpriorityafter choosing the right element - Drupal Media Pipeline — how Drupal generates these patterns via responsive image styles
- Reference: MDN Responsive Images Guide
- Reference: web.dev Responsive Images