HTML5 Mastery: The Complete Web Foundation
HomeInsightsCoursesHTMLResponsive Images (srcset, picture)
Images & Graphics

Responsive Images (srcset, picture)

Serve the perfect image for every device and screen resolution. Master srcset, sizes, and the picture element for optimal performance and visual quality.

The Responsive Image Problem

Traditional <img src="..."> serves the same image to all devices - wasting bandwidth on mobile and delivering low-quality images on high-DPI displays.

4x

data wasted serving desktop images to mobile

2-3x

pixel density on modern screens (Retina/HDPI)

60%

of web traffic is mobile (2024)

❌ Problem: One Size For All

HTML
<img src="image-4000px.jpg" alt="Photo">

<!-- Issues: --&gt;
<!-- - Mobile downloads 4MB file for 400px display --&gt;
<!-- - High-DPI screens show blurry image --&gt;
<!-- - Waste of bandwidth and loading time --&gt;

✅ Solution: Responsive Images

HTML
<img src="image-400.jpg"
     srcset="image-400.jpg 400w,
             image-800.jpg 800w,
             image-1600.jpg 1600w"
     sizes="(max-width: 600px) 400px, 800px"
     alt="Photo">

<!-- Browser downloads the right size! --&gt;

The srcset Attribute

srcset provides multiple image sources. Browser chooses based on device capabilities.

1. Resolution Switching (w descriptor)

HTML
<img src="photo-400.jpg"
     srcset="photo-400.jpg 400w,
             photo-800.jpg 800w,
             photo-1200.jpg 1200w,
             photo-1600.jpg 1600w"
     sizes="100vw"
     alt="Landscape photo"
     width="1600"
     height="900">

<!-- How it works: --&gt;
<!-- - w = actual width of image file in pixels --&gt;
<!-- - 400w means image is 400 pixels wide --&gt;
<!-- - Browser picks based on screen size & DPI --&gt;

Understanding 'w' Descriptor:

  • 400w = "This image file is 400 pixels wide"
  • NOT a media query
  • Browser calculates: needed_width / dpr
  • Then selects closest match from srcset

Real-World Example:

Device: iPhone with 375px viewport, 3x DPR

Calculation: 375px × 3 = 1125px needed

Browser selects: photo-1200.jpg (closest larger)

2. Pixel Density Switching (x descriptor)

HTML
<img src="icon.png"
     srcset="icon-1x.png 1x,
             icon-2x.png 2x,
             icon-3x.png 3x"
     alt="Icon"
     width="100"
     height="100">

<!-- Use for: --&gt;
<!-- - Fixed-size images (icons, logos) --&gt;
<!-- - When you know exact display size --&gt;

<!-- Examples: --&gt;
<!-- 1x = Standard displays (72-96 DPI) --&gt;
<!-- 2x = Retina/HD displays (144-192 DPI) --&gt;
<!-- 3x = Super Retina (216+ DPI) --&gt;

The sizes Attribute

sizes tells the browser how much space the image will take in the layout.

Basic sizes Syntax
HTML
<img src="photo.jpg"
     srcset="photo-400.jpg 400w,
             photo-800.jpg 800w,
             photo-1200.jpg 1200w"
     sizes="(max-width: 600px) 400px,
            (max-width: 1000px) 800px,
            1200px"
     alt="Photo">

<!-- Reads as: --&gt;
<!-- "If viewport ≤ 600px, image will be 400px wide" --&gt;
<!-- "If viewport ≤ 1000px, image will be 800px wide" --&gt;
<!-- "Otherwise, image will be 1200px wide" --&gt;

Common sizes Patterns:

Full Width Image
HTML
<img src="hero.jpg"
     srcset="hero-800.jpg 800w, hero-1600.jpg 1600w"
     sizes="100vw"
     alt="Hero image">
Half Width on Desktop
HTML
<img src="photo.jpg"
     srcset="photo-400.jpg 400w, photo-800.jpg 800w"
     sizes="(min-width: 800px) 50vw, 100vw"
     alt="Photo">
Grid Layout
HTML
<img src="thumbnail.jpg"
     srcset="thumb-200.jpg 200w, thumb-400.jpg 400w"
     sizes="(min-width: 1200px) 25vw,
            (min-width: 800px) 33vw,
            (min-width: 500px) 50vw,
            100vw"
     alt="Thumbnail">

<!-- 4 columns on large screens (25%) --&gt;
<!-- 3 columns on medium screens (33%) --&gt;
<!-- 2 columns on small screens (50%) --&gt;
<!-- 1 column on mobile (100%) --&gt;
Max Width Container
HTML
<img src="content.jpg"
     srcset="content-600.jpg 600w, content-800.jpg 800w"
     sizes="(min-width: 1000px) 800px, 90vw"
     alt="Content image">

<!-- Fixed 800px on wide screens --&gt;
<!-- 90% of viewport on smaller screens --&gt;

The <picture> Element

<picture> provides full control: different images for different scenarios.

1. Art Direction

Different crops/compositions for different screen sizes:

HTML
<picture>
    <!-- Wide landscape for desktop --&gt;
    <source media="(min-width: 1000px)" 
            srcset="hero-wide.jpg">
    
    <!-- Square crop for tablets --&gt;
    <source media="(min-width: 600px)" 
            srcset="hero-square.jpg">
    
    <!-- Vertical crop for mobile (portrait-focused) --&gt;
    <img src="hero-vertical.jpg" 
         alt="Hero image">
</picture>

<!-- Use when: --&gt;
<!-- - Focal point changes (portrait vs landscape) --&gt;
<!-- - Different aspect ratios needed --&gt;
<!-- - Important content must stay visible --&gt;

2. Format Switching

Serve modern formats with fallbacks:

HTML
<picture>
    <!-- Try AVIF first (best compression) --&gt;
    <source type="image/avif" 
            srcset="photo.avif">
    
    <!-- Try WebP (good compression) --&gt;
    <source type="image/webp" 
            srcset="photo.webp">
    
    <!-- Fallback to JPEG (universal) --&gt;
    <img src="photo.jpg" 
         alt="Photo">
</picture>

3. Combining Everything

Format + Resolution + Art Direction
HTML
<picture>
    <!-- Desktop: AVIF, multiple resolutions --&gt;
    <source media="(min-width: 1000px)"
            type="image/avif"
            srcset="desktop-800.avif 800w,
                    desktop-1600.avif 1600w"
            sizes="1000px">
    
    <!-- Desktop: WebP fallback --&gt;
    <source media="(min-width: 1000px)"
            type="image/webp"
            srcset="desktop-800.webp 800w,
                    desktop-1600.webp 1600w"
            sizes="1000px">
    
    <!-- Mobile: AVIF, smaller images --&gt;
    <source type="image/avif"
            srcset="mobile-400.avif 400w,
                    mobile-800.avif 800w"
            sizes="100vw">
    
    <!-- Mobile: WebP fallback --&gt;
    <source type="image/webp"
            srcset="mobile-400.webp 400w,
                    mobile-800.webp 800w"
            sizes="100vw">
    
    <!-- Universal fallback --&gt;
    <img src="mobile-400.jpg"
         srcset="mobile-400.jpg 400w,
                 mobile-800.jpg 800w"
         sizes="100vw"
         alt="Photo"
         width="800"
         height="600"
         loading="lazy">
</picture>

Loading Strategies

1. Lazy Loading

HTML
<!-- Lazy load below-the-fold images --&gt;
<img src="photo.jpg"
     srcset="photo-400.jpg 400w, photo-800.jpg 800w"
     sizes="100vw"
     loading="lazy"
     alt="Photo">

<!-- loading attribute: --&gt;
<!-- - lazy: Load when near viewport (default for most browsers) --&gt;
<!-- - eager: Load immediately --&gt;
<!-- - auto: Browser decides --&gt;

2. Preload Critical Images

HTML
<head>
    <!-- Preload hero image for instant display --&gt;
    <link rel="preload" 
          as="image" 
          href="hero.jpg"
          imagesrcset="hero-800.jpg 800w, hero-1600.jpg 1600w"
          imagesizes="100vw">
</head>

<body>
    <img src="hero.jpg"
         srcset="hero-800.jpg 800w, hero-1600.jpg 1600w"
         sizes="100vw"
         alt="Hero"
         loading="eager">
</body>

3. Decoding Strategy

HTML
<!-- Async decoding (don't block rendering) --&gt;
<img src="large-photo.jpg" 
     decoding="async" 
     alt="Photo">

<!-- sync: Block rendering until decoded --&gt;
<!-- async: Decode asynchronously (recommended for large images) --&gt;
<!-- auto: Browser decides --&gt;

Practical Examples

Blog Post Featured Image

HTML
<picture>
    <source type="image/webp"
            srcset="featured-400.webp 400w,
                    featured-800.webp 800w,
                    featured-1200.webp 1200w"
            sizes="(min-width: 1000px) 800px, 90vw">
    
    <img src="featured-800.jpg"
         srcset="featured-400.jpg 400w,
                 featured-800.jpg 800w,
                 featured-1200.jpg 1200w"
         sizes="(min-width: 1000px) 800px, 90vw"
         alt="Article featured image"
         width="1200"
         height="675"
         loading="lazy">
</picture>

Product Thumbnail Grid

HTML
<img src="product-thumb.jpg"
     srcset="product-thumb-200.jpg 200w,
             product-thumb-400.jpg 400w"
     sizes="(min-width: 1200px) 20vw,
            (min-width: 800px) 25vw,
            (min-width: 500px) 33vw,
            50vw"
     alt="Product name"
     width="400"
     height="400"
     loading="lazy">

Hero Banner with Art Direction

HTML
<picture>
    <!-- Desktop: wide 21:9 crop --&gt;
    <source media="(min-width: 1200px)"
            type="image/webp"
            srcset="hero-wide-1920.webp 1920w,
                    hero-wide-2560.webp 2560w"
            sizes="100vw">
    
    <!-- Tablet: 16:9 crop --&gt;
    <source media="(min-width: 768px)"
            type="image/webp"
            srcset="hero-tablet-1024.webp 1024w,
                    hero-tablet-1536.webp 1536w"
            sizes="100vw">
    
    <!-- Mobile: 4:5 portrait crop --&gt;
    <img src="hero-mobile-750.jpg"
         srcset="hero-mobile-750.jpg 750w,
                 hero-mobile-1125.jpg 1125w"
         sizes="100vw"
         alt="Hero image"
         width="750"
         height="937"
         loading="eager">
</picture>

Best Practices

✅ Do:

  • Always specify width and height (prevents layout shift)
  • Use loading="lazy" for below-the-fold images
  • Provide at least 3-4 size variants (small, medium, large, xlarge)
  • Use WebP/AVIF with JPEG/PNG fallback
  • Set sizes to match your actual layout
  • Compress all images before upload
  • Use CDN for automatic image optimization
  • Test on real devices with throttled connection

❌ Don't:

  • Forget the fallback src attribute
  • Use srcset without sizes (except for x descriptor)
  • Provide only 1-2 image sizes (too limited)
  • Set sizes to incorrect values
  • Lazy load above-the-fold images
  • Skip width/height attributes
  • Serve unoptimized original images

Testing & Debugging

  • Chrome DevTools: Network tab shows which image was downloaded
  • Responsive Mode: Test different viewports and DPR
  • Lighthouse: Audits image sizing and format
  • Real Devices: Test on actual mobile/tablet
  • Network Throttling: Simulate slow connections
Debug in Browser Console
JAVASCRIPT
// Check which image was loaded
const img = document.querySelector('img');
console.log('Current src:', img.currentSrc);
console.log('Natural size:', img.naturalWidth, 'x', img.naturalHeight);

// Check device pixel ratio
console.log('Device pixel ratio:', window.devicePixelRatio);

Automation Tools

  • Cloudinary: Automatic responsive images via URL parameters
  • imgix: Real-time image processing and optimization
  • Sharp (Node.js): Generate multiple sizes programmatically
  • Responsive Images Breakpoints Generator: Calculate optimal breakpoints
  • Webpack/Vite plugins: Automatic image optimization in build

What's Next?

Master more advanced image techniques including image maps and SVG graphics.