Why can't one image file suit every screen?
The bike shop's layout shows the workshop photo across the column. On a phone that column is about 328 CSS pixels wide. On a desktop the same photo might fill 860 pixels, and on a 2x laptop screen that means about 1,720 real pixels. One file can't be right for both: the 1,800 pixel copy is sharp on the laptop and wastes most of its 560 KB on the phone.
The previous lesson picked one size for a phone. Real sites need to serve phones, tablets and desktops from the same HTML, and the srcset and sizes attributes on <img> let you list several files and leave the choice to each browser.
What does a `w` descriptor tell the browser?
The srcset attribute is a comma-separated list of files. After each file name comes its real width in pixels, followed by the letter w. The browser can't know a file's width before downloading it, so you write it down.
Keep src too. Browsers that support srcset, which is all current ones, ignore it when the list is present, and it is still a sensible fallback and what some tools read. Pointing it at a middle size such as 800 pixels is a reasonable choice.
<img src="/img/workshop-800.jpg"
srcset="/img/workshop-480.jpg 480w,
/img/workshop-800.jpg 800w,
/img/workshop-1200.jpg 1200w,
/img/workshop-1800.jpg 1800w"
sizes="(min-width: 900px) 860px, calc(100vw - 32px)"
alt="A bike on the repair stand in our workshop"
width="1800" height="1200">Why does the browser need `sizes` as well?
The browser picks an image long before it has applied your CSS. The preload scanner sees the <img> tag in the first bytes of HTML and wants to start the download straight away, so it can't wait to find out how wide the image will be laid out. The sizes attribute tells it in advance.
A sizes value is a list of conditions and widths. The browser uses the first condition that matches the screen, and the last entry, with no condition, is the default. In the example, a screen at least 900 pixels wide gets 860 pixels; anything narrower gets the screen width minus 32 pixels of margin.
Leave sizes out and the browser assumes 100vw, the full width of the screen. For an image that fills the column on a phone that is close enough, but for an image in a sidebar or a two-column grid it overestimates and downloads too much.
How does the phone pick a file?
It multiplies the slot width from sizes by its device pixel ratio, then picks a file at least that wide. On the simulated phone: 360 − 32 = 328 CSS pixels, times 2, makes 656. The smallest file in the list that covers 656 is the 800 pixel copy, so that is what downloads: 92 KB instead of 560 KB.
A desktop at 2x with a 1,280 pixel window gets 860 × 2 = 1,720 and picks the 1,800 pixel file. A tablet lands somewhere between. You write the list once, and every device gets a sensible file.
Real browsers have some freedom here. They may take a smaller file on a slow connection or when data saving is on, and they may reuse a larger file already in the cache instead of fetching a smaller one. The simulator always applies the plain rule, which is also what most browsers do most of the time.


What goes wrong with `sizes` most often?
Writing the slot in real pixels instead of CSS pixels. The browser does the multiplication itself, so sizes="1600px" means "this image is 1,600 CSS pixels wide", and on a 2x phone it asks for 3,200 real pixels. That is the first bug on the hire page.
The second is a sizes value that no longer matches the CSS. A design changes from one column to two, the image halves in width, and sizes still says 100vw. Nothing looks wrong, the image is sharp, and every phone downloads twice what it needs. Whenever you change an image's layout, check its sizes.
The third is a wrong w number. The browser trusts what you write and never checks it against the file. If two labels are swapped, as on the tandem, it downloads the big file while believing it is small.
When should you use `x` descriptors instead?
For images that are always the same fixed size, such as an 80 pixel logo or an avatar, x descriptors are simpler. List a normal file as 1x and a double-size file as 2x, and leave out sizes. The browser picks by its pixel ratio alone.
Use w descriptors and sizes whenever the image width changes with the screen, which is most photos in a fluid layout. You can't mix w and x in the same srcset.
<img src="/img/logo-80.png"
srcset="/img/logo-80.png 1x, /img/logo-160.png 2x"
alt="Spoke and Chain" width="80" height="80">How is this different from `<picture>`?
With srcset and sizes, every file is the same picture at a different size, and the browser is free to choose. With <picture> and <source media>, you make the choice: show a tight crop of the bike on narrow screens and the wide shot of the whole workshop on large ones. That is called art direction.
The two combine well. Each <source> can have its own srcset and sizes, so you can offer AVIF and WebP in several widths, with a JPEG <img> as the fallback. Start with plain srcset on the <img>; reach for <picture> when you need a different crop or a format fallback.
How do you check what the browser picked?
In Chrome DevTools, select the <img> in the Elements panel and look at the currentSrc property in the Properties tab, or hover the image to see its rendered and intrinsic sizes. The Network panel shows which single file was downloaded. Use device mode to try different phone widths and pixel ratios, and clear the cache between tries, because a larger cached file can be reused.
Lighthouse flags images that are much larger than their displayed size, which often means a missing or wrong sizes. In this simulator, the Waterfall shows which file the phone took, and the Vitals view warns when it is far wider than the slot.
What do AI tools get wrong about responsive images?
AI-written markup often includes a srcset with no sizes, or a sizes copied from somewhere else that doesn't match the page's CSS. It also invents files: a srcset listing -400, -800 and -1600 copies that were never made, so the browser requests files that don't exist. Check that every file in the list is real and that its w number is its true width.
Ask an assistant to write sizes for a specific layout and it may produce a long, confident list of breakpoints. Read each one against your CSS, then test at two or three widths in DevTools to make sure the browser picks what you expect.
Field notes
- One image file cannot suit both a small phone and a wide desktop screen.
srcsetwithwdescriptors tells the browser each file's real width.sizestells the browser how wide the image will show, in CSS pixels.- The browser multiplies the slot width by its pixel ratio and picks a file.
Easy to get wrong
sizes is in CSS pixels. The browser multiplies by the pixel ratio itself, so a value in real pixels makes it download a file twice as wide as needed or more.
Without sizes the browser assumes the image fills the whole screen, which is wrong for a grid and wastes bytes on every visit.
The browser trusts the list. A missing file shows a broken image on exactly the devices that picked it, which you may never test.
Questions people ask
How many sizes should I make?
Three to five widths cover most layouts, for example 480, 800, 1,200 and 1,800 pixels. Steps of roughly 1.5 times keep each device close to its ideal without filling the server with near-duplicates.
Do I still need `width` and `height` with `srcset`?
Yes. They give the browser the image's shape before any file arrives, so its space is kept. Use the size of any of the files, as long as they share the same shape.
What is `sizes="auto"`?
For images with loading="lazy", newer Chrome versions accept sizes="auto", which uses the laid-out width because the image waits for layout anyway. It doesn't work for eager images, and support is not universal yet, so add a normal sizes after it as a fallback.
Does the simulator copy a real browser's choice exactly?
It applies the standard rule: slot width from sizes, times 2, then the smallest file that covers it. It doesn't model data saving or reuse of larger cached files, which real browsers sometimes do.