Responsive Images and the Sizes Attribute Most Sites Skip
I audited a product landing page last month where the hero image weighed 1.9 MB on a phone. The developers had done the "right" thing: they added srcset with five widths from 400 to 2400 pixels.

I audited a product landing page last month where the hero image weighed 1.9 MB on a phone. The developers had done the "right" thing: they added srcset with five widths from 400 to 2400 pixels. The browser still downloaded the 2400 pixel file on a 390 pixel wide screen, every single time.
The cause was one missing attribute. Responsive images on the web depend on two attributes working together, srcset and sizes, and most teams only write the first. This article explains how the browser actually picks a file, why the sizes attribute matters more than people think, and how to check in a few minutes if your images are doing what you intended.
How Browsers Choose a Responsive Image
When the browser meets an img element with a srcset list of widths, it needs two facts before it can choose a file: how wide the image will be displayed, and the device pixel ratio of the screen. The pixel ratio it knows. The display width it does not, at least not early enough.
Image requests start while the HTML is still being parsed, often before the CSS has loaded. At that moment the browser has no layout, so it cannot measure your image. The sizes attribute is how you tell it in advance. Without sizes, the browser assumes the image will be as wide as the whole viewport, which is the default value of 100vw.
Then it multiplies. A phone 390 CSS pixels wide with a pixel ratio of 3 asks for roughly 1170 pixels. If your largest candidate is 2400 and the next one down is 1200, it picks 1200. That alone is often bigger than needed when the image only fills half the screen.
Writing a sizes Attribute That Matches Your Layout
The sizes attribute is a list of media conditions paired with widths, read from left to right. The browser uses the first condition that matches. A typical value for a card grid reads like this in plain words: "on screens at least 1024 pixels wide, the image is 33vw; on screens at least 640 pixels wide, it is 50vw; otherwise it is 100vw minus 32 pixels of padding."
To write it, open the page and note the image's rendered width at each of your layout breakpoints. You do not need pixel precision. Getting within 10 to 15 percent is enough, because the browser rounds up to the next candidate anyway.
| Layout | Rendered width | sizes entry |
|---|---|---|
| Phone, single column | Full width minus padding | calc(100vw minus 32px) |
| Tablet, two columns | About half the screen | 50vw |
| Desktop, three columns in a 1200px container | About 380px | 380px |
Notice the last row uses a fixed pixel width. Once the container stops growing, the image stops growing too, so a vw value would overestimate it on very large monitors.
Picking the Widths in srcset
More candidates is not automatically better. Each width is a separate file to generate, store and cache. For most layouts, four or five widths cover everything:
- A small file around 400 pixels for low density phones and thumbnails.
- A middle file around 800 pixels, the workhorse for most phones at 2x.
- A large file around 1200 pixels for 3x phones and tablets.
- One or two wider files, 1600 and 2000, only if the image can be displayed full width on desktop.
Steps of roughly 400 pixels keep the wasted bytes small. A practical rule: if two neighbouring files differ by less than 20 percent in size on disk, drop one of them.
The "Always Serve Retina Images" Habit Costs More Than It Gives
A lot of front end advice says to always serve images at the full device pixel ratio so they look crisp on high density screens. On a 3x phone, that means three times the width and roughly nine times the pixels. I think this is usually a bad trade.
Most people cannot tell the difference between a photo served at 2x and one at 3x on a phone held at normal distance. The byte difference is not subtle at all. On the landing page from the introduction, capping photographs at 2x density cut the hero image from 410 KB to 190 KB with no visible change in a side by side test with five colleagues.
Browsers are allowed to pick a smaller candidate than the exact match, and some do on slow connections, but you cannot rely on that. Capping the largest file you offer is the dependable way to control it. Line art, logos and text in images are the exception. Those show blur quickly, and they are usually better as SVG anyway.
When to Use the picture Element Instead
srcset with sizes handles one job: the same image at different resolutions. It is the right tool roughly nine times out of ten. Reach for the picture element only in two cases.
Art direction
When the crop needs to change, such as a wide landscape banner on desktop that becomes a tight portrait crop on phones, picture with media conditions lets you swap entirely different images.
Format fallbacks
When you want to offer AVIF or WebP with a JPEG fallback, picture with type attributes lets each browser take the best format it supports. Browser support for modern formats is now broad, so many teams serve WebP alone, but older Safari versions still benefit from a fallback.
Checking Your Images in DevTools
You do not need a performance tool to verify this. Open the page in a desktop browser, then:
- Turn on device emulation and pick a phone with a pixel ratio of 2 or 3.
- Open the Network panel, filter by images and enable "Disable cache" so you see fresh requests. Our piece on how browser caching decides what reloads explains why a cached file can hide the problem.
- Reload and look at which file names were downloaded for each image.
- In the Elements panel, select the image and check the currentSrc property in the properties view. It shows which candidate the browser chose.
If the chosen file is more than about 1.5 times the rendered width times the pixel ratio, your sizes value is too generous. The Network panel has more useful views like this, covered in our guide to DevTools features developers never open.
FAQ
Do I still need width and height attributes with srcset?
Yes. They let the browser reserve space before the image loads, which prevents layout shift. Use the intrinsic dimensions of any candidate, since only the ratio matters.
Does lazy loading change how srcset works?
No. Lazy loading only delays when the request starts. The browser still uses sizes and the pixel ratio to pick a candidate once the image approaches the viewport.
Can I set sizes to auto?
For lazy loaded images, recent Chromium browsers accept sizes="auto" and measure the real layout width. Other browsers ignore it, so keep a normal value after auto as a fallback.
The Practical Takeaway
Pick your three heaviest images, add a sizes attribute that matches their real layout widths, cap photographs at 2x density and confirm the chosen file with currentSrc in DevTools. That small pass usually cuts image weight on phones by a third or more. Find more layout and CSS guides in our front end section.
More in Front-End
Front-End
How CSS Cascade Layers Tame a Growing Stylesheet
Every large stylesheet eventually reaches the same unhappy place.
Front-End
Why position: sticky Stops Working and How to Find the Cause
You add position: sticky and top: 0 to a header, scroll the page, and it slides away with everything else as if you had written...
Front-End
Why CSS Specificity Battles Make Stylesheets Harder to Maintain
A new rule gets added to override one small visual detail, it does not work, so another rule gets stacked on top with a more...