Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.
ImagesHigh

Use srcset for responsive images

Images wider than 100px use the srcset attribute to offer multiple resolution variants, letting the browser download the optimal size for the user's viewport and device pixel ratio.

Utilities
Quick take
Typical fix time 15 min
  • Add `srcset` with width descriptors (`400w`, `800w`) to all images wider than ~100px
  • Always pair `srcset` width descriptors with a `sizes` attribute—without `sizes` the browser assumes `100vw`
  • Use density descriptors (`1x`, `2x`) only for fixed-size images like avatars and icons
  • Keep `src` pointing to a fallback size for browsers that don't support `srcset`
Why it matters: Without `srcset`, every user—regardless of their device—downloads the same large image. A 1600px hero image served to a 375px mobile display downloads 4x the data needed. The `srcset` attribute lets browsers choose the optimal file automatically, reducing bandwidth by 50-80% for mobile users with no change to visual quality.

Rule Details

srcset lets browsers choose the best image size for the user's viewport and screen density. Combined with sizes, it is the standard HTML mechanism for responsive images.

Code Examples

Width Descriptors (w) — for variable-size images

Use when the image's display size varies with the viewport width. Requires a sizes attribute.

<img
  src="photo-800w.jpg"
  srcset="
    photo-400w.jpg   400w,
    photo-800w.jpg   800w,
    photo-1200w.jpg 1200w,
    photo-1600w.jpg 1600w
  "
  sizes="(max-width: 600px) 100vw,
         (max-width: 1200px) 50vw,
         800px"
  alt="Mountain landscape"
  width="1600"
  height="1067"
  loading="lazy"
>

Density Descriptors (x) — for fixed-size images

Use for images that are always the same CSS size but should be sharp on high-DPI screens.

<!-- Fixed 48px avatar — sharp on Retina displays -->
<img
  src="avatar.jpg"
  srcset="
    avatar.jpg    1x,
    avatar-2x.jpg 2x,
    avatar-3x.jpg 3x
  "
  alt="Jane Doe"
  width="48"
  height="48"
>

Why It Matters

Without srcset, every user—regardless of their device—downloads the same large image. A 1600px hero image served to a 375px mobile display downloads 4x the data needed. The srcset attribute lets browsers choose the optimal file automatically, reducing bandwidth by 50-80% for mobile users with no change to visual quality.

The sizes Attribute in Detail

sizes is a comma-separated list of media condition + size pairs. The browser uses the first matching condition.

<!-- Pattern: (media-condition) size, ..., default-size -->
<img
  srcset="photo-400w.jpg 400w, photo-800w.jpg 800w, photo-1200w.jpg 1200w"
  sizes="
    (max-width: 480px) calc(100vw - 32px),
    (max-width: 768px) calc(50vw - 24px),
    (max-width: 1200px) 33vw,
    400px
  "
  src="photo-800w.jpg"
  alt="Gallery photo"
  width="1200"
  height="800"
  loading="lazy"
>
The sizes attribute must reflect actual CSS layout

Inaccurate sizes values cause the browser to pick the wrong srcset candidate. If your layout changes via CSS (grid, flexbox, media queries), sizes must match. Use browser DevTools to verify the computed image width at each breakpoint.

Common Layout Patterns

<!-- Full-width banner -->
<img
  srcset="banner-800w.jpg 800w, banner-1200w.jpg 1200w, banner-1600w.jpg 1600w"
  sizes="100vw"
  src="banner-1200w.jpg"
  alt="Banner"
  width="1600"
  height="400"
>
 
<!-- 2-column grid: full-width mobile, half on desktop -->
<img
  srcset="card-400w.jpg 400w, card-800w.jpg 800w"
  sizes="(max-width: 768px) 100vw, 50vw"
  src="card-800w.jpg"
  alt="Card image"
  width="800"
  height="600"
  loading="lazy"
>
 
<!-- 3-column grid: full-width mobile, 1/3 on desktop (with gap) -->
<img
  srcset="thumb-300w.jpg 300w, thumb-600w.jpg 600w"
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, calc(33.33vw - 16px)"
  src="thumb-600w.jpg"
  alt="Thumbnail"
  width="600"
  height="400"
  loading="lazy"
>

picture + srcset for Format + Size

Combine <picture> for format selection with srcset/sizes for responsive sizing.

<picture>
  <source
    type="image/avif"
    srcset="photo-400w.avif 400w, photo-800w.avif 800w, photo-1200w.avif 1200w"
    sizes="(max-width: 600px) 100vw, 50vw"
  >
  <source
    type="image/webp"
    srcset="photo-400w.webp 400w, photo-800w.webp 800w, photo-1200w.webp 1200w"
    sizes="(max-width: 600px) 100vw, 50vw"
  >
  <img
    src="photo-800w.jpg"
    srcset="photo-400w.jpg 400w, photo-800w.jpg 800w, photo-1200w.jpg 1200w"
    sizes="(max-width: 600px) 100vw, 50vw"
    alt="Photo description"
    width="1200"
    height="800"
    loading="lazy"
  >
</picture>

How the Browser Selects a Candidate

The browser algorithm (simplified):

  1. Parse sizes to find the first matching condition → compute effective display width (e.g., 600px)
  2. Multiply by device pixel ratio (e.g., 2x Retina → 1200px needed)
  3. Find the srcset candidate closest to 1200px without going significantly under
  4. Download that candidate
// Simulate browser candidate selection (for debugging)
function selectCandidate(srcset, sizes, viewportWidth, dpr = 1) {
  // Parse sizes to find effective width
  const effectiveWidth = viewportWidth // Simplified; real implementation parses media conditions
 
  // Target pixel width
  const targetPx = effectiveWidth * dpr
 
  // Parse srcset candidates
  const candidates = srcset.trim().split(',').map(s => {
    const [url, width] = s.trim().split(/\s+/)
    return { url, width: parseInt(width) }
  })
 
  // Pick closest candidate >= targetPx
  const sorted = candidates.sort((a, b) => a.width - b.width)
  return sorted.find(c => c.width >= targetPx) || sorted[sorted.length - 1]
}

Verification

Automated Checks

  • Run Lighthouse — "Use responsive images" and "Properly size images" audits flag missing srcset
  • Use RespImageLint (opens in new tab) bookmarklet — reports which srcset candidate the browser selected vs what was optimal
  • Open Chrome DevTools → Network → filter Img → resize the viewport — observe which srcset candidate loads
  • Test on a device with 2x pixel ratio — verify a 2x variant is selected when available

Manual Checks

  • Verify the rendered or user-facing behavior manually in a representative browser or runtime flow.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Scan all <img> elements in this codebase. Flag: 1) Any <img> wider than 100px (in CSS or layout) that lacks a srcset attribute. 2) Any <img> with srcset but missing a sizes attribute (necessary for width descriptors). 3) Any <img> using only density descriptors (1x, 2x) where width descriptors (400w, 800w) would be more appropriate. 4) Any srcset that provides only one size (no benefit over plain src). Report each finding with file path and line number.

Fix

Auto-fix issues

For each <img> missing srcset: 1) Generate multiple width variants: typically 400w, 800w, 1200w (add 1600w for full-bleed images). 2) Add srcset listing each variant with its width descriptor. 3) Add a sizes attribute describing actual CSS layout width at each breakpoint. 4) Keep src pointing to a mid-range size (800w) as the fallback. 5) Add width and height attributes if missing. For each image, show the complete corrected HTML including srcset and sizes.

Explain

Learn more

Explain how srcset and the two descriptor types work. Width descriptors (400w, 800w) describe the intrinsic width of each candidate; the browser combines this with the sizes attribute to calculate which candidate provides the best pixel density for the current viewport. Density descriptors (1x, 2x) are simpler but less flexible—use them only for fixed-size images like avatars and icons. The sizes attribute is critical: without it, the browser assumes 100vw and always downloads the largest candidate even on narrow screens.

Review

Code review

Review image assets, markup, and delivery configuration related to Use srcset for responsive images. Flag exact files or components where format choice, sizing, or loading behavior violates the rule, and describe how to confirm the fix in DevTools.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Lighthousedeveloper.chrome.comTool
RespImageLintausi.github.ioTool

Rules that often go hand-in-hand with this one.

Serve images at the correct display size

Images are not significantly larger than their display dimensions—serving a 2000px image for a 400px container wastes bandwidth and hurts LCP.

Images
Use <picture> with an <img> fallback

Every <picture> element contains a required <img> fallback as its last child, ensuring images display in all browsers including those that don't support <picture>.

Images
Set explicit width and height on images

All <img> elements have explicit width and height attributes so browsers can reserve space before the image loads, preventing layout shift.

Images
Lazy load offscreen images

Images below the visible viewport use loading="lazy" to defer download until the user scrolls near them, reducing initial page load time.

Images

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385