Responsive images explained: srcset, sizes and picture

One image file cannot serve a phone and a desktop well. These attributes let the browser choose, and the one people get wrong is sizes.

Which tool for which job

srcset with w
Same image at several widths. Needs sizes to work properly.
srcset with x
Same image for 1×/2× screens at a fixed display size. No sizes needed.
sizes
Tells the browser how wide the image will be laid out, before CSS is parsed.
picture + source type
Format switching — WebP or AVIF with a JPEG fallback.
picture + source media
Art direction — a different crop at different breakpoints.
Most common mistake
Omitting sizes, so the browser assumes 100vw and downloads too much.

Implementing it

  1. Step 1: Generate the widths you need

    Three or four is plenty for most layouts — something like 640, 1024, 1600 and 2048 pixels wide. More than that adds build time and storage for savings nobody measures.

  2. Step 2: List them in srcset with their real widths

    Each entry is a URL followed by the file's actual width in pixels and a w. The browser uses these to decide which file is worth downloading — the numbers must be true or the choice will be wrong.

  3. Step 3: Describe the layout with sizes

    Tell the browser how wide the image will be at each breakpoint, in CSS units. Without this it assumes the image fills the viewport and downloads a far larger file than a narrow column needs.

  4. Step 4: Wrap in picture only if you need format or art direction

    Use source type to offer WebP or AVIF with a fallback, and source media to serve a genuinely different crop on small screens. For plain resolution switching, srcset alone is enough.

The details that matter

The basic pattern

<img
  src="photo-1024.jpg"
  srcset="photo-640.jpg 640w,
          photo-1024.jpg 1024w,
          photo-1600.jpg 1600w,
          photo-2048.jpg 2048w"
  sizes="(min-width: 1100px) 800px,
         (min-width: 700px) 60vw,
         100vw"
  width="1600" height="1067"
  alt="A description of what the image shows">

The src is the fallback for anything that does not understand srcset. The width and height give the aspect ratio so the browser reserves space. The alt is not optional.

Why sizes exists, and why it is so often wrong

The browser starts downloading images before it has finished parsing CSS. At that moment it does not know your image will sit in an 800-pixel column — so it needs to be told, and that is what sizes is.

Omit it and the browser assumes 100vw: the image fills the viewport. On a wide desktop it will then pick your 2048-pixel file for a column that is 800 pixels across, which is exactly the waste srcset was meant to prevent.

The value is a list of media conditions with the width the image will occupy, ending in a default with no condition. It describes layout, not the image — and it must be kept in step with your CSS, which is the reason it drifts out of date on real sites.

w descriptors or x descriptors?

Use w when the image's displayed size varies with the viewport — nearly all content images. It works together with sizes.

Use x when the display size is fixed and only pixel density varies — logos, icons, avatars. srcset="logo.png 1x, logo@2x.png 2x" needs no sizes at all, because the browser already knows how big it will be.

Mixing the two in one srcset is invalid. Pick the one that matches how the image is laid out.

picture is for two different jobs

Format switching. Offer a modern format with a fallback; the browser takes the first source it understands.

<picture>
  <source srcset="photo.avif" type="image/avif">
  <source srcset="photo.webp" type="image/webp">
  <img src="photo.jpg" alt="…" width="1600" height="1067">
</picture>

Art direction. Serve a genuinely different image — a tighter crop on a phone, where a wide landscape shot would render the subject unrecognisably small. Use media on the sources.

If you only need the same image at different sizes, you do not need picture. Plain srcset on an img is simpler and does the job.

Mistakes that quietly cost you

  • Wrong widths in srcset. The descriptors must match the files' real widths, or the browser's arithmetic is wrong and it picks badly.
  • sizes out of step with the CSS. A layout change that does not update sizes silently reintroduces oversized downloads.
  • No width and height. Responsive images that still shift the layout as they load.
  • Lazy-loading the hero. Responsive or not, this delays your LCP element.
  • Too many widths. Eight variants is build cost and storage for a saving nobody can measure. Three or four is plenty.

Do it here instead

These run in your browser on any device, so nothing is uploaded and there is nothing to install.

  • Resize image

    By pixels or percentage, aspect ratio locked, across a whole batch.

    Open the tool
  • Bulk compressor

    One setting across a whole folder, processed in parallel, downloaded as a ZIP.

    Open the tool
  • JPG to WebP

    Typically 25–35% smaller at matched quality. The standard web win.

    Open the tool
  • Compress to 200KB

    Full resolution kept, quality still invisible to the eye.

    Open the tool

Questions

Frequently asked questions

Every answer below is present in the page source, expanded, so it can be read without opening anything.

What is the difference between srcset and sizes?

srcset lists the image files available and how wide each one is. sizes tells the browser how wide the image will be laid out on the page. The browser needs both to choose sensibly — with srcset alone it assumes the image fills the viewport.

Do I need the picture element?

Only for format switching or art direction. To offer WebP with a JPEG fallback, or to serve a different crop on small screens, yes. For the same image at several sizes, srcset on a plain img is simpler and does everything you need.

How many image widths should I generate?

Three or four covers almost every layout — something like 640, 1024, 1600 and 2048. Beyond that you are adding build time and storage for savings too small to measure.

What happens if I omit sizes?

The browser assumes 100vw — that the image fills the viewport width. On a wide screen it will then download your largest file for an image sitting in a narrow column, which defeats the purpose of srcset entirely.

Do responsive images help Core Web Vitals?

Yes, through LCP: a phone downloading a 640-pixel file instead of a 2048-pixel one gets its largest element far sooner. They do nothing for CLS on their own — you still need width and height to reserve the space.

Does this work with lazy loading?

Yes, they are independent. Combine loading="lazy" with srcset for images below the fold. Keep both off — the lazy attribute at least — for your LCP image.

From the blog

Related reading

More background from the blog.

  • Practical Guides

    How to Compress Images for a Website Without Wrecking Them

    Resize before you compress. Measured on one photograph: half the width at quality 80 produced a 16.4 KB file, while full width at quality 40 produced 20.6 KB and looked far worse. The order of operations matters more than the settings.

    8 min read

  • Image Formats

    WebP vs JPEG: Which Should You Use, and When JPEG Still Wins

    WebP produced a 27.0 KB file against MozJPEG's 41.1 KB on the same photograph at slightly higher measured fidelity — 34% smaller. It also produced a larger file than JPEG on random noise. The advantage is real and content-dependent.

    9 min read

  • Image Compression

    JPEG Quality Settings Explained: What the Number Actually Means

    Quality 80 is not 80% of anything. It is an index into a quantisation table. Measured on one photograph: dropping from 100 to 90 removes 77% of the file and 4.2 dB of fidelity; dropping from 40 to 30 removes 17% and costs almost as much.

    8 min read