Imageshighresponsive
Implement responsive images with srcset
rule · responsive-images
Responsive images deliver the right size image for each device, saving bandwidth and improving performance.
Code Example
HTML
<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w,
image-1600.jpg 1600w
"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
33vw"
alt="Responsive image"
width="800"
height="600"
>Why It Matters
Serving a 2000px image to a 400px phone screen wastes bandwidth and slows loading—srcset lets the browser download only the size it needs.
Understanding sizes Attribute
| sizes Value | Meaning |
|---|---|
100vw | Image takes full viewport width |
50vw | Image takes half viewport width |
(max-width: 600px) 100vw | Full width on screens up to 600px |
33vw | Default when no condition matches |
srcset with Pixel Density (x) Descriptors
HTML
<!-- For fixed-size images (icons, logos) -->
<img
src="logo.png"
srcset="
logo.png 1x,
logo@2x.png 2x,
logo@3x.png 3x
"
alt="Logo"
width="200"
height="50"
>Picture Element for Art Direction
HTML
<picture>
<!-- Different crop for mobile -->
<source
media="(max-width: 600px)"
srcset="hero-mobile.webp 600w, hero-mobile-2x.webp 1200w"
sizes="100vw"
type="image/webp"
>
<!-- Desktop version -->
<source
media="(min-width: 601px)"
srcset="hero-desktop.webp 1200w, hero-desktop-2x.webp 2400w"
sizes="100vw"
type="image/webp"
>
<!-- Fallback -->
<img src="hero-desktop.jpg" alt="Hero" width="1200" height="600">
</picture>React Responsive Image Component
TSX
interface ResponsiveImageProps {
src: string
alt: string
sizes: string
widths?: number[]
className?: string
}
function ResponsiveImage({
src,
alt,
sizes,
widths = [400, 800, 1200, 1600],
className
}: ResponsiveImageProps) {
const srcSet = widths
.map(w => `${getImageUrl(src, w)} ${w}w`)
.join(', ')
return (
<img
src={getImageUrl(src, widths[1])} // Default to medium size
srcSet={srcSet}
sizes={sizes}
alt={alt}
loading="lazy"
decoding="async"
className={className}
/>
)
}
function getImageUrl(src: string, width: number): string {
// Example: append width parameter for CDN
return `${src}?w=${width}`
}
// Usage
<ResponsiveImage
src="/images/hero.jpg"
alt="Hero image"
sizes="(max-width: 768px) 100vw, 50vw"
widths={[320, 640, 960, 1280]}
/>Next.js Automatic Responsive Images
TSX
import Image from 'next/image'
function ProductCard({ product }: { product: Product }) {
return (
<Image
src={product.image}
alt={product.name}
width={400}
height={300}
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw"
// Next.js automatically generates srcset
/>
)
}Common sizes Patterns
HTML
<!-- Full width hero -->
<img sizes="100vw" ...>
<!-- Two-column layout on desktop -->
<img sizes="(min-width: 1024px) 50vw, 100vw" ...>
<!-- Three-column grid -->
<img sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw" ...>
<!-- Fixed sidebar, fluid main -->
<img sizes="(min-width: 1024px) calc(100vw - 300px), 100vw" ...>Generating Multiple Sizes
JavaScript
// Build script to generate image sizes
const sharp = require('sharp')
const sizes = [400, 800, 1200, 1600, 2000]
async function generateSizes(inputPath, outputDir) {
for (const width of sizes) {
await sharp(inputPath)
.resize(width)
.webp({ quality: 80 })
.toFile(`${outputDir}/image-${width}.webp`)
}
}Browser Selection Logic
The browser considers:
- Viewport width
- Device pixel ratio (DPR)
sizesattribute value- Available
srcsetoptions
TEXT
Example: 400px viewport, 2x DPR, sizes="100vw"
- Needs: 400px × 2 = 800 CSS pixels worth of image data
- Browser selects: image-800.jpg (or next larger)Testing
- Open DevTools Network tab, filter by Images
- Resize viewport—different sizes should load
- Check which srcset variant was selected in Network details
- Test on real mobile devices (different DPRs)
- Use Lighthouse to verify responsive images are implemented
Verification
Automated Checks
- Re-test key pages in DevTools or WebPageTest after deployment to confirm the CDN or image component preserves the responsive variants.
Manual Checks
- Confirm the browser selects smaller image candidates on narrow viewports and higher-density variants on retina screens.
- Check that the
sizesattribute matches the real CSS layout; if the layout changes, updatesizesin the same PR. - Verify you are not sending a
1600wimage to a viewport that only renders it at a few hundred CSS pixels.