TESTED · IMAGE WORKFLOWS
Responsive images with srcset: a working example you can download
Fix oversized or blurry responsive images with real srcset files, a layout-specific sizes hint, and observed Chrome width and pixel-density selections.
If a responsive image loads a file that seems too large or too small, check two numbers first: the image’s display width on the page and the device pixel ratio. The sizes attribute tells the browser the expected display width. Each w value in srcset must describe the actual width of its file. You can then inspect currentSrc to see which file the browser selected.
The example below uses three versions of one illustration. It gives the browser accurate file widths and describes a layout that is 32 pixels narrower than the viewport on small screens, then 640 pixels wide on larger screens.

Download the working example
Download the complete image demo ZIP, or save responsive.html and all three images: trail-640.jpg, trail-1280.jpg, and trail-1920.jpg. Save them together in one folder without changing their filenames, then open the HTML file in a browser. If you serve the page from your site instead, keep the same relative file arrangement.
The key markup is:
<img
id="responsive"
src="trail-640.jpg"
srcset="
trail-640.jpg 640w,
trail-1280.jpg 1280w,
trail-1920.jpg 1920w
"
sizes="(max-width: 700px) calc(100vw - 32px), 640px"
width="1920"
height="1200"
alt="Trail sign pointing left to River loop and right to Hill lookout"
>
The three JPEGs are actually 640 × 400, 1280 × 800, and 1920 × 1200 pixels. Their w descriptions match those measured widths. All three share an 8:5 aspect ratio and show the same composition. The width and height attributes provide that ratio so the page can reserve space while the image loads; they do not mean every candidate is 1920 pixels wide.
The example’s image CSS makes the displayed image fill the available width through a 700-pixel viewport, with 16-pixel page margins on each side. From 701 pixels upward, it displays the image at 640 pixels wide:
#responsive {
width: 100%;
}
@media (min-width: 701px) {
#responsive {
width: 640px;
}
}
That is why sizes uses calc(100vw - 32px) for the smaller layout and 640px for the larger one. CSS controls how wide the image appears. sizes describes that width to the browser when it considers the files in srcset. Keep the two descriptions in agreement when you adapt the example to your own layout.
Which file did Chrome select?
Fresh local Chrome loads of the example produced these results. “Slot” is the image’s displayed width in CSS pixels.
| Viewport width | Device pixel ratio | Slot | Selected file |
|---|---|---|---|
| 390 px | 1 | 358 px | trail-640.jpg |
| 390 px | 2 | 358 px | trail-1280.jpg |
| 1440 px | 1 | 640 px | trail-640.jpg |
| 1440 px | 2 | 640 px | trail-1280.jpg |
| 1440 px | 3 | 640 px | trail-1920.jpg |
Multiplying the slot by the device pixel ratio gives useful comparison points: 358, 716, 640, 1280, and 1920 pixels for those five rows. These selections describe the recorded Chrome loads. Browser selection can also depend on factors such as network conditions and cache state.
The 390-pixel viewport shows why viewport width alone does not identify the file. Both runs displayed the image at 358 CSS pixels, but the run at device pixel ratio 2 selected the 1280-pixel JPEG. At a 1440-pixel viewport, the image remained 640 CSS pixels wide; increasing device pixel ratio changed the selected file.
Check the file your page uses
Open your page, then paste this into the browser’s DevTools Console. If you changed the example’s id, update the selector.
const image = document.querySelector("#responsive");
({
currentSrc: image.currentSrc,
devicePixelRatio: window.devicePixelRatio,
displayWidth: image.getBoundingClientRect().width,
naturalWidth: image.naturalWidth,
loaded: image.complete && image.naturalWidth > 0
});
currentSrc gives the selected URL. It does not, by itself, confirm that the image loaded. The loaded check pairs complete with a positive naturalWidth because complete can also be true for a broken image.
Do not use naturalWidth to identify the downloaded file’s encoded width. With these w candidates, it can report a density-corrected width in CSS pixels. In the 390-pixel, device-pixel-ratio-1 run, currentSrc named trail-640.jpg, while naturalWidth was 358. The file itself measured 640 pixels wide. Use currentSrc for the selected filename and the file’s actual metadata when checking whether a w label is truthful.
For a clean comparison after changing viewport size or device pixel ratio, load the page in a fresh browser context. A resize in an existing context can leave a previously selected, larger resource in use and confuse the comparison.
Fix a size that seems wrong
Start with the rendered width. If your CSS caps an image at 640 pixels on desktop, but sizes says 100vw, a 1440-pixel-wide viewport reports a 1440-pixel slot even though the image displays at 640 pixels. Update sizes to describe the real CSS slot. The example uses 640px after its breakpoint for precisely that reason.
Next, check every candidate’s actual pixel width. Renaming a 640-pixel file does not make it a 1200-pixel candidate. A false 1200w label gives the browser false information about that file. Keep the descriptor types consistent within this srcset; this example uses width descriptors (w), not density descriptors (x).
Finally, confirm the selected URL and that the image loaded. The src="trail-640.jpg" attribute does not force the browser to use the 640-pixel file when it can evaluate the width candidates. Likewise, a naturalWidth value of 640 does not prove that trail-640.jpg was downloaded. Read currentSrc, check the load state, and compare the named file with its actual dimensions.
Adapt the example to a smaller blog card
A thumbnail and a full article illustration often use the same picture in different spaces. If your card is 320 CSS pixels wide on desktop, the final slot in that card’s sizes should describe 320 pixels. Do not borrow the article image’s 640-pixel hint solely because the files share a subject. The HTML needs information about this placement.
You can work out that value from the browser instead of estimating from a screenshot. Select the card image in the Elements panel and read its rendered width, or run getBoundingClientRect().width on that element. Then inspect the CSS rule that limits it. If the card grows and shrinks across a row of columns, use the actual layout’s breakpoint and available column space in the hint. The download demonstrates one fixed desktop slot; it does not contain a tested multi-column card layout.
Keep the composition consistent when making size alternatives. The supplied three JPEGs all contain both destinations. If a small candidate crops away the Hill lookout sign, choosing that file would change the image’s meaning. A change of crop for a different layout is a separate editorial choice; a width label alone says nothing about which signs the file retains.
Source note: The selections above come from fresh local contexts in Chrome 154.0.8037.93 on October 7, 2026. Other browser engines were not tested. The markup guidance is consistent with MDN’s references for the img element, currentSrc, and naturalWidth.