Responsive Images

โ–ถ Try It Yourself

Responsive Images

1. Introduction

Images are typically the largest assets on a webpage and the single biggest contributor to slow load times. A 2000-pixel image served to a 400-pixel mobile screen wastes bandwidth, drains battery, and slows Core Web Vitals scores. HTML5 provides two powerful mechanisms โ€” the srcset attribute and the <picture> element โ€” that let the browser automatically select the most appropriate image based on screen size, resolution, and format support. This lesson teaches you to use both correctly.

2. Concept

Responsive Image Techniques

Technique Element / Attribute Best For
Resolution switching img srcset + sizes Same image, different sizes for different screens
Art direction <picture> + <source media> Different crops/compositions at different breakpoints
Format switching <picture> + <source type> Serve modern formats (WebP, AVIF) with fallback
Lazy loading img loading=”lazy” Defer off-screen images until near viewport
Note: srcset tells the browser which image files are available. The sizes attribute tells it how wide the image will be displayed at each viewport width. The browser then picks the optimal file โ€” you cannot force which one it chooses, and that is intentional (it may also factor in cached images and network conditions).
Tip: Always specify width and height attributes on <img> elements even with responsive images. The browser uses these to reserve space before the image loads, preventing Cumulative Layout Shift (CLS) โ€” one of Google’s Core Web Vitals metrics.
Warning: Never use loading="lazy" on above-the-fold images (the hero image, logo, or any image visible without scrolling). Lazy loading delays these critical images, hurting Largest Contentful Paint (LCP). Only apply it to images below the fold.

3. Basic Example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Images Demo</title>
  </head>
  <body>

    <!-- Resolution switching: srcset + sizes -->
    <img
      src="/images/hero-800.jpg"
      srcset="
        /images/hero-400.jpg   400w,
        /images/hero-800.jpg   800w,
        /images/hero-1200.jpg 1200w,
        /images/hero-1600.jpg 1600w
      "
      sizes="
        (max-width: 600px)  100vw,
        (max-width: 960px)   80vw,
        1200px
      "
      alt="Mountain landscape at sunrise with mist in the valley"
      width="1600" height="900"
    >

    <!-- Art direction: different crop at different viewport widths -->
    <picture>
      <source
        media="(max-width: 600px)"
        srcset="/images/product-portrait-400.jpg 1x, /images/product-portrait-800.jpg 2x"
      >
      <source
        media="(max-width: 960px)"
        srcset="/images/product-square-600.jpg 1x, /images/product-square-1200.jpg 2x"
      >
      <img
        src="/images/product-landscape-1200.jpg"
        alt="ProBook 15 laptop open on a desk showing the display"
        width="1200" height="675"
        loading="lazy"
      >
    </picture>

    <!-- Format switching: AVIF โ†’ WebP โ†’ JPEG fallback -->
    <picture>
      <source type="image/avif" srcset="/images/hero.avif">
      <source type="image/webp" srcset="/images/hero.webp">
      <img
        src="/images/hero.jpg"
        alt="Aerial view of a coastal city at sunset"
        width="1200" height="630"
        loading="lazy"
      >
    </picture>

  </body>
</html>

4. How It Works

Step 1 โ€” srcset with Width Descriptors

Each entry in srcset pairs a URL with a width descriptor (w). This tells the browser the intrinsic pixel width of each file. The browser then uses the sizes attribute to calculate which file to download based on the current viewport and device pixel ratio.

Step 2 โ€” sizes Attribute

sizes is a comma-separated list of media conditions and slot widths. The browser evaluates them top-to-bottom and uses the first matching condition’s slot width. The final entry (no media condition) is the default. This tells the browser how wide the image will actually be rendered โ€” essential for it to pick the right srcset entry.

Step 3 โ€” picture for Art Direction

<picture> wraps multiple <source> elements and a fallback <img>. The browser uses the first <source> whose media condition matches. This allows completely different image crops or compositions at different breakpoints โ€” not just different resolutions of the same image.

Step 4 โ€” Format Switching

<source type="image/avif"> serves AVIF (typically 50% smaller than JPEG) to browsers that support it. Unsupported browsers fall through to WebP, then to the JPEG fallback <img>. This gives modern browsers maximum compression with zero developer burden on old browsers.

5. Real-World Example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TechStore โ€” ProBook 15</title>
    <style>
      .product-hero { width: 100%; max-width: 900px; display: block; }
      .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; margin-top: 1rem; }
      .gallery img { width: 100%; height: auto; border-radius: 4px; }
    </style>
  </head>
  <body>
    <main>
      <h1>ProBook 15 Laptop</h1>

      <!-- Hero: AVIF/WebP + art direction + LCP image (no lazy) -->
      <picture>
        <source
          media="(max-width: 480px)"
          type="image/avif"
          srcset="/products/probook15-mobile.avif"
        >
        <source
          media="(max-width: 480px)"
          type="image/webp"
          srcset="/products/probook15-mobile.webp"
        >
        <source
          type="image/avif"
          srcset="/products/probook15-desktop.avif"
        >
        <source
          type="image/webp"
          srcset="/products/probook15-desktop.webp"
        >
        <img
          class="product-hero"
          src="/products/probook15-desktop.jpg"
          alt="ProBook 15 laptop open at 90 degrees on a white desk, display showing a code editor"
          width="900" height="600"
          fetchpriority="high"
        >
      </picture>

      <!-- Gallery: lazy loading, srcset for HiDPI screens -->
      <div class="gallery">
        <img
          src="/products/probook15-side.jpg"
          srcset="/products/probook15-side.jpg 1x, /products/probook15-side@2x.jpg 2x"
          alt="ProBook 15 viewed from the side showing its thin profile"
          width="400" height="300"
          loading="lazy"
        >
        <img
          src="/products/probook15-keyboard.jpg"
          srcset="/products/probook15-keyboard.jpg 1x, /products/probook15-keyboard@2x.jpg 2x"
          alt="Backlit keyboard of the ProBook 15 in a dark environment"
          width="400" height="300"
          loading="lazy"
        >
        <img
          src="/products/probook15-ports.jpg"
          srcset="/products/probook15-ports.jpg 1x, /products/probook15-ports@2x.jpg 2x"
          alt="Left side showing 2x USB-A, 1x USB-C, HDMI, and SD card reader ports"
          width="400" height="300"
          loading="lazy"
        >
      </div>
    </main>
  </body>
</html>

6. Common Mistakes

Serving one large image to all devices

<img src="/images/hero-3000.jpg" alt="Hero" width="3000" height="1600">

Provide multiple sizes with srcset for efficient delivery

<img
  src="/images/hero-800.jpg"
  srcset="/images/hero-400.jpg 400w, /images/hero-800.jpg 800w, /images/hero-1600.jpg 1600w"
  sizes="(max-width:600px) 100vw, 800px"
  alt="Hero" width="1600" height="900"
>

lazy loading the LCP image (hurts Largest Contentful Paint)

<img src="/hero.jpg" alt="Hero" loading="lazy">

Use fetchpriority=”high” on LCP images instead

<img src="/hero.jpg" alt="Hero" fetchpriority="high">

7. Try It Yourself

▶ Try It Yourself

8. Quick Reference

Attribute / Element Purpose Notes
img srcset (w descriptors) List of available image widths Pair with sizes attribute
img sizes Display width at each breakpoint Browser uses to pick srcset entry
<picture> + <source media> Art direction โ€” different images per breakpoint First matching source wins
<source type> Format switching (AVIF, WebP, JPEG) Modern formats with graceful fallback
loading=”lazy” Defer off-screen images Never on above-fold / LCP images
fetchpriority=”high” Prioritise LCP image download Use on hero/above-fold image
width + height attributes Reserve space before load Prevents CLS โ€” always include

9. Quiz

🧠 Test Yourself

Why should you never use loading=”lazy” on above-the-fold hero images?





โ–ถ Try It Yourself