The Intelligence Layer.

Expert movements in image optimization, web performance, and the technical decisions that drive high-conversion digital experiences.

Responsive Images in 2026: The Complete srcset, sizes, and picture Guide
Tutorials

Responsive Images in 2026: The Complete srcset, sizes, and picture Guide

Serving one image to every device is the most common performance anti-pattern on the web. Here's the definitive guide to srcset, sizes, and the picture element—with rules for when to use each, common mistakes, and copy-paste patterns that actually work.

TinyImage Team

Lead Architect

April 29, 2026

Published

7 min

Read time

Topics

responsive imagessrcsetpicture elementhtmlweb development

Table of Contents

Responsive Images in 2026: The Complete srcset, sizes, and picture Guide

Most developers have a rough understanding of responsive images and just well enough that they don't look it up, but not well enough to avoid the common mistakes. The result is a codebase full of srcset attributes that are technically present but functionally wrong—either because sizes was omitted, or because the breakpoints don't match the actual layout, or because the browser's caching behavior interacted with the implementation in ways the developer didn't anticipate.

This guide covers how responsive images actually work, what each tool does, and—critically—what happens when you get it wrong.


Why a Single Image Doesn't Scale

The fundamental problem is easy to understand but easy to underestimate in practice.

A 2400×1600px hero image that serves your desktop layout downloads at full resolution on a 375px-wide iPhone screen. That's approximately 4× the pixel data the device needs, for zero visual benefit.

On a 4G connection, a 600KB JPEG might take 800ms to download. The same image served at 375px width, optimized for mobile, could be 80KB—a 100ms download. That 700ms difference is the margin between a "Good" LCP score and a "Needs Improvement" one.

At scale—100 product images on a category page—the math becomes significant enough that it's visible in your Google Analytics bounce rate.


The Three Tools and When to Use Each

srcset — Resolution Switching

Use srcset when you have the same image at multiple sizes and want the browser to pick the right one.

<img
  src="photo-800.jpg"
  srcset="
    photo-400.jpg   400w,
    photo-800.jpg   800w,
    photo-1200.jpg 1200w,
    photo-1600.jpg 1600w
  "
  alt="Mountain trail at dawn"
/>

The browser evaluates: "What is my viewport width? What is the device pixel ratio? Given those, which source from srcset will cover the rendered image width at native resolution?"

Without the sizes attribute (next section), the browser assumes the image will render at 100vw—the full viewport width. This assumption is wrong for almost every image that isn't a full-width hero. If you omit sizes on a thumbnail that renders at 200px, the browser downloads an 800px image for a 2× display instead of the 400px image that would have been sufficient.

The key rule: srcset alone is correct only for full-viewport-width images. For anything else, add sizes.

sizes — Layout Hints

The sizes attribute communicates to the browser how large the image will appear at different viewport widths. The browser uses this to calculate which srcset entry to download before the CSS is parsed.

That distinction matters: the browser selects the image source in the <head>, before rendering, because it needs to start downloading images as early as possible. It can't wait for CSS to load and compute layout. So you're telling it the layout in advance.

<img
  src="photo-800.jpg"
  srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
  sizes="
    (max-width: 600px) 100vw,
    (max-width: 1200px) 50vw,
    800px
  "
  alt="Campaign photo"
/>

Reading this sizes attribute:

  • At viewport ≤ 600px: The image renders at 100% of viewport width.
  • At viewport ≤ 1200px: The image renders at 50% of viewport width.
  • Otherwise: The image renders at exactly 800px wide.

The browser takes the rendered width, multiplies by the device pixel ratio (2 for Retina), and selects the smallest srcset entry that covers that computed width.

The most common mistake: Writing sizes values that don't match your CSS. If your CSS puts the image in a 6-column grid on desktop that renders at 400px, but your sizes attribute says 1200px for desktop viewports, the browser downloads a 1200px image. Your sizes attribute must be accurate.

<picture> — Art Direction and Format Switching

<picture> is for two distinct use cases. It's important not to conflate them.

Use case 1: Format negotiation (preferred approach)

Serve AVIF to browsers that support it, WebP to the rest, and JPEG as a universal fallback—without JavaScript.

<picture>
  <source srcset="photo.avif" type="image/avif" />
  <source srcset="photo.webp" type="image/webp" />
  <img src="photo.jpg" alt="Mountain trail at dawn" width="1200" height="800" />
</picture>

The browser reads the <source> elements in order and uses the first one whose type it supports. The <img> tag is the mandatory fallback and carries all the attributes that apply in every case (alt, width, height, loading, etc.).

Use case 2: Art direction (different crop per device)

A landscape crop looks great on desktop. The same image shown at 375px wide may be indecipherable—the subject is tiny. Art direction means serving a tighter, portrait-oriented crop on mobile.

<picture>
  <!-- Mobile: portrait crop, tightly framed on the subject -->
  <source
    media="(max-width: 600px)"
    srcset="hero-mobile-portrait.avif"
    type="image/avif"
  />
  <source
    media="(max-width: 600px)"
    srcset="hero-mobile-portrait.webp"
    type="image/webp"
  />

  <!-- Desktop: wide landscape crop -->
  <source srcset="hero-desktop-landscape.avif" type="image/avif" />
  <source srcset="hero-desktop-landscape.webp" type="image/webp" />

  <img
    src="hero-desktop-landscape.jpg"
    alt="Campaign hero"
    width="1200"
    height="630"
  />
</picture>

The Full Pattern: All Three Combined

This is the production-ready pattern for a responsive image using modern formats, multiple resolutions, and CLS prevention.

<picture>
  <!-- AVIF: modern browsers, 3 resolutions -->
  <source
    type="image/avif"
    srcset="photo-400.avif 400w, photo-800.avif 800w, photo-1200.avif 1200w"
    sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
  />

  <!-- WebP: broad fallback, 3 resolutions -->
  <source
    type="image/webp"
    srcset="photo-400.webp 400w, photo-800.webp 800w, photo-1200.webp 1200w"
    sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
  />

  <!-- JPEG: universal final fallback -->
  <img
    src="photo-800.jpg"
    srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
    sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
    alt="Descriptive alternative text"
    width="800"
    height="534"
    loading="lazy"
  />
</picture>

Yes, it's verbose. This is why component libraries and server-side image optimization pipelines exist.


How to Generate Responsive Variants

Generating three sizes × three formats = nine image files per source, for every image, is not something you want to do manually.

For individual images: TinyImage's browser-based converter can generate multiple sizes and formats from a single source in one pass. Files are processed locally—no upload required.

For Next.js projects: The <Image> component handles responsive variants automatically. Provide the largest source and the component generates appropriately-sized variants on demand.

For build pipelines: Tools like sharp (Node.js), Cloudinary's transform URLs, or Vercel/Netlify image optimization APIs generate responsive variants on the fly or at build time.


The loading and fetchpriority Attributes

Two more attributes that complete the responsive image setup:

  • loading="lazy" — defers download until the image is near the viewport. Use on everything below the fold.
  • loading="eager" — the default. Use explicitly on above-fold images to make intent clear.
  • fetchpriority="high" — signals that this image is important and should be fetched before other resources. Use on your LCP image.
<!-- LCP hero image: eager + high priority -->
<img
  src="hero.avif"
  alt="Homepage hero"
  width="1200"
  height="630"
  loading="eager"
  fetchpriority="high"
/>

<!-- Below-fold content image: lazy -->
<img
  src="blog-thumbnail.avif"
  alt="Blog post thumbnail"
  width="400"
  height="266"
  loading="lazy"
/>

The goal of responsive images isn't to be clever about HTML. It's to ensure that every user on every device downloads precisely as much image data as they need to see a sharp, clear rendering—no more. Get this right and your bandwidth costs drop, your Lighthouse scores improve, and your LCP moves in the right direction. Get it wrong and you have a pile of srcset attributes that do nothing useful.

Check your implementation by throttling to "Slow 4G" in Chrome DevTools and watching the Network tab. If the browser downloads a 1200px image for a viewport that renders it at 300px, your sizes attributes need attention.

Deploy Visual Excellence

Put what you've learned into practice with TinyImage.Online - the free, privacy-focused image compression tool that works entirely in your browser.

TinyImage Team

contact@tinyimage.online