Kodokon kodokon.com

Responsive images: srcset, sizes, and picture

Serve the right image to the right screen with srcset and sizes, reserve picture for art direction, and master lazy loading.

9 min · 3 questions

Open this lesson in Kodokon

Images typically account for half of a page's weight: they are your first performance lever. The core mechanism is srcset with w descriptors, which declares the actual widths of the available files, paired with sizes, which announces the intended display width. Key point: the browser picks the image via its preload scanner, before it has even computed the CSS layout. Without sizes, it assumes 100vw and often downloads far more than needed. The final choice also factors in screen density: a 2x screen over a 400 px area will fetch a file of roughly 800 px.

HTML
<img src="harbor-800.jpg"
     srcset="harbor-400.jpg 400w,
             harbor-800.jpg 800w,
             harbor-1600.jpg 1600w"
     sizes="(max-width: 600px) 100vw, 50vw"
     alt="Old harbor at sunset"
     width="800" height="533">
w descriptors + sizes: the browser optimizes on its own

With srcset/sizes, the browser stays in charge: it may serve a lighter variant depending on density or data-saving preferences. <picture> flips the logic: you impose the source via <source media> or <source type>. Reserve it for two cases: art direction (a different crop on mobile, not just a size) and format negotiation (AVIF or WebP with a JPEG fallback). For a simple resize, picture is needless complexity.

HTML
<picture>
  <source media="(max-width: 600px)"
          srcset="hero-square.jpg">
  <source type="image/avif"
          srcset="hero-wide.avif">
  <img src="hero-wide.jpg" alt="Museum facade"
       width="1200" height="600">
</picture>
picture: mobile recrop and a modern format with fallback

For everything else on the page, loading="lazy" defers the download until the image approaches the viewport, natively and without JavaScript. Pair it with decoding="async" so decoding does not block rendering, and above all with width and height: the browser derives the aspect ratio and reserves the space, which eliminates layout shifts (CLS). For the critical image, do the opposite of lazy: fetchpriority="high".

HTML
<img src="gallery-01.jpg"
     alt="Exhibition hall"
     loading="lazy" decoding="async"
     width="800" height="600">
Off-screen image: lazy, async, and reserved dimensions

Knowledge check

Make sure you remember the key points of this lesson.

  1. What exactly is the sizes attribute for?
    • Resizing the image on the CSS side
    • Announcing the intended display width so the browser can choose from srcset
    • Capping the maximum weight of the downloaded file
  2. In which case is <picture> required rather than a plain srcset?
    • To serve the same image at several widths
    • For a different crop per screen or a modern format with fallback
    • To enable native lazy loading
  3. What is the effect of loading="lazy" on your hero image visible at load?
    • None, the attribute is ignored above the fold
    • It delays its loading and hurts LCP
    • It improves LCP by prioritizing the image