Responsive images: srcset, sizes and picture explained

How srcset, sizes and the picture element let each device download the right image, how to write sizes correctly, and the mistakes that waste bytes.

IntermediateHow-to6 min readUpdated October 30, 2026

In this guide
  1. The three problems it solves
  2. Different sizes: srcset with widths and sizes
  3. Writing sizes correctly
  4. Fixed-size images: srcset with densities
  5. Different crops or formats: picture
  6. How many sizes, and which
  7. What WordPress does
  8. Checking your work
  9. Common mistakes
  10. Check your own page
  11. Sources

The three problems it solves

A single image file cannot be right for every visitor. Three different problems get lumped together:

  1. Different sizes. The picture is shown wider on a desktop than on a phone. A phone should not download the desktop file.
  2. Different screen sharpness. A phone with a sharp screen needs more pixels to look crisp at the same size. A standard screen does not.
  3. Different crops. On a phone, a wide banner might need a tighter crop so the faces stay visible. This is called art direction.

HTML has a feature for each. The first two use srcset and sizes on an img element, and the third uses picture.

Different sizes: srcset with widths and sizes

You list the files that exist and how wide each one is. You also tell the browser how wide the image will be on screen, because the browser has to decide before the CSS has loaded.

<img src="/img/photo-800.jpg"
     srcset="/img/photo-400.jpg 400w, /img/photo-800.jpg 800w, /img/photo-1200.jpg 1200w, /img/photo-1600.jpg 1600w"
     sizes="(min-width: 1000px) 50vw, 100vw"
     width="1600" height="1067" alt="Description of the photo">

The browser multiplies the displayed width by the screen's pixel density and picks the smallest file in the list that is at least that wide. On a 400-pixel-wide phone with a pixel density of 3, the image fills the screen, so the browser wants about 1,200 pixels and downloads photo-1200.jpg. A desktop window of 1,400 pixels with a density of 1 shows the image at 700 pixels and downloads photo-800.jpg. You never tell the browser which file to use; you describe the choices and the layout, and it decides. It may also choose a larger file that is already in its cache.

Writing sizes correctly

sizes is where most errors are. If you leave it out, the browser assumes the image is as wide as the viewport (100vw) and downloads a file that is too large for any image that does not fill the screen. Match sizes to your CSS:

Layoutsizes
Fills the screen width100vw (or leave sizes out)
Half the screen on large screens, full width on small(min-width: 800px) 50vw, 100vw
Three columns on large screens, one on small(min-width: 1000px) 33vw, 100vw
A column with a fixed maximum width of 700 pixels(min-width: 700px) 700px, 100vw
A column with padding of 20 pixels on both sidescalc(100vw - 40px)

Units in sizes are relative to the viewport, not to the image. Percentages are not allowed, so use vw for widths that follow the screen and pixels for fixed ones. Conditions are checked from the first to the last, and the first one that is true wins, so put the largest screens first.

Lazy-loaded images have a shortcut. The keyword auto lets the browser use the image's real layout width, once layout is known. It is only valid together with loading="lazy", and the browser falls back to the other values if it does not understand it. It works in Chrome 126 and later, in Firefox 150, and partially in Safari 27, so keep a fallback after auto:

<img src="/img/photo-800.jpg" srcset="/img/photo-400.jpg 400w, /img/photo-800.jpg 800w, /img/photo-1600.jpg 1600w"
     sizes="auto, (min-width: 1000px) 50vw, 100vw" loading="lazy" width="1600" height="1067" alt="Description">

Fixed-size images: srcset with densities

When the image is always shown at the same size, such as a logo or an avatar, you do not need sizes. List the same picture at different densities, and the browser picks the one that matches the screen:

<img src="/img/logo-160.png" srcset="/img/logo-160.png 1x, /img/logo-320.png 2x, /img/logo-480.png 3x" width="160" height="48" alt="Company name">

Do not mix w and x descriptors in one list. Pick one kind per image.

Different crops or formats: picture

The picture element holds several source elements and one img. The browser takes the first source whose condition matches. Use it for a different crop on small screens, or to offer modern formats with a fallback:

<picture>
  <source media="(max-width: 600px)" srcset="/img/banner-narrow.avif" type="image/avif">
  <source media="(max-width: 600px)" srcset="/img/banner-narrow.jpg">
  <source srcset="/img/banner-wide.avif" type="image/avif">
  <img src="/img/banner-wide.jpg" width="1600" height="600" alt="The shop front">
</picture>

The img inside is required, carries the alt text and does the real work: the sources only choose which file it shows. See WebP and AVIF for formats. When the crops have different shapes, give each source its own width and height so the reserved space matches (image dimensions).

How many sizes, and which

Three to five widths cover most images. A reasonable set for a content image is 400, 800, 1200 and 1600 pixels, and add 2000 or more only for full-width heroes on large screens. Fewer than three misses the savings; more than six adds files without adding choice. The widest file should match the widest the image is ever shown at, doubled for sharp screens, and no wider: do not publish a 4,000-pixel file for an image that never exceeds 1,000 pixels on screen.

What WordPress does

Images that you insert in the editor get srcset and sizes automatically, because core generates several sizes when you upload and writes the attributes for you. The default sizes it writes is (max-width: Wpx) 100vw, Wpx, where W is the width of the image as inserted. That is correct for an image that fills the width of a column up to a maximum, and wrong when your theme lays the image out in a different way, such as three columns. If your theme does that, it should filter the sizes value, and you can check by reading the HTML.

Checking your work

  1. Hover or inspect the image in Chrome DevTools: the tooltip shows the rendered size and the intrinsic size of the file that was loaded. If the file is much bigger than the rendered size, sizes or the list is wrong.
  2. Read `currentSrc` in the console to see which file the browser chose: select the image in the Elements panel and run $0.currentSrc.
  3. Resize the window and reload to see different files chosen.
  4. Run Lighthouse. In Lighthouse 13, the insight "Improve image delivery" says when a file is larger than it needs to be for its displayed dimensions, and gives both numbers.

Common mistakes

Check your own page

Run the free audit and look at the image findings for files larger than their displayed size. Fix the largest first. If an image has srcset but is still too big, the sizes hint is almost always the cause.

Sources

Start here

Free website audit

Live Google data for any public page: mobile and desktop.

Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it

Rather have this done for you? Speed optimization · Book a call

Related guides

All guides →