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.
In this guide
The three problems it solves
A single image file cannot be right for every visitor. Three different problems get lumped together:
- Different sizes. The picture is shown wider on a desktop than on a phone. A phone should not download the desktop file.
- Different screen sharpness. A phone with a sharp screen needs more pixels to look crisp at the same size. A standard screen does not.
- 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">- `srcset` is a comma-separated list. Each entry is a file name, a space, and the file's real width in pixels with a
w. Thewnumber must match the file's actual width. - `sizes` is a list of media conditions and the width the image will have when each is true, ending with a default. Here: "on screens 1,000 pixels wide or more, the image is half the viewport wide; otherwise it fills it".
- `src` is the fallback for browsers that do not understand
srcset, and the file the browser uses when nothing else applies.
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:
| Layout | sizes |
|---|---|
| Fills the screen width | 100vw (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 sides | calc(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
- 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,
sizesor the list is wrong. - Read `currentSrc` in the console to see which file the browser chose: select the image in the Elements panel and run
$0.currentSrc. - Resize the window and reload to see different files chosen.
- 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
- No `sizes` on an image that does not fill the screen.
- A `w` number that does not match the file. The browser trusts it.
- One giant `src` and no `srcset`. Every visitor downloads the big file.
- Different shapes in one `srcset`. The reserved space is wrong for some sizes. Use
picturefor different crops. - A fixed `width` in CSS that does not match
sizes. The browser chooses from the hint, not from the CSS. - Upscaling. Offering a 2,000-pixel version made from a 1,000-pixel original adds bytes and no detail.
- Forgetting the dimensions. The page shifts when the image loads.
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
- Responsive images on MDN: resolution switching, art direction and how browsers choose.
- The img element on MDN:
srcset,sizes, theautokeyword and the default of100vw. - WordPress core: media.php: how WordPress writes
srcsetandsizes.
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