srcset and sizes for Product Images That Don't Waste Bandwidth
Measure the real slot width at each breakpoint, write sizes from it, and check which file loads at each screen density.
A product grid is where wasted image bytes pile up: many images, on every page, on phones. The way to write a good sizes is to measure the slot first, not to guess it.
Step 1: measure the slot at each breakpoint
The grid is one column under 600px, two from 600px, and four from 960px, with a 16px gap and 16px page padding. Instead of working out the arithmetic, read the real width in the browser:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
for width in (390, 768, 1280):
page = browser.new_page(viewport={"width": width, "height": 800})
page.goto("http://127.0.0.1:8765/srcset.html")
slot = page.evaluate("document.querySelector('img').getBoundingClientRect().width")
print(f"viewport {width}px: image slot {round(slot)}px")
browser.close()
viewport 390px: image slot 342px
viewport 768px: image slot 352px
viewport 1280px: image slot 296px
Slots are narrower than 100vw, 50vw and 25vw because of the padding and gaps. For sizes, the nearest simple value is fine: it is only used to pick a file, not to lay anything out.
Step 2: write sizes from those numbers
<img
src="product-800.jpg"
srcset="product-400.jpg 400w, product-800.jpg 800w, product-1200.jpg 1200w"
sizes="(min-width: 960px) 25vw, (min-width: 600px) 50vw, 100vw"
width="800" height="480" alt="Blue product tile">
Step 3: check which file loads on each screen
const img = document.querySelector("img");
console.log(img.currentSrc, img.getBoundingClientRect().width, devicePixelRatio);
Run it after resizing the window, or on a phone through remote debugging. The result I got, for the same page at different densities:
390px, DPR 2 slot 342px -> product-800.jpg
390px, DPR 3 slot 342px -> product-1200.jpg
1280px, DPR 1 slot 296px -> product-400.jpg
1280px, DPR 2 slot 296px -> product-800.jpg
A 342px slot at DPR 2 needs 684 physical pixels, so the 800w file is the smallest that covers it. At DPR 3 it needs 1026, so the 1200w file loads.
What a wrong sizes costs
In an earlier version of the same grid the columns stretched to 608px at 1280px, but sizes still said 25vw. At DPR 1, Chrome picked product-400.jpg and stretched it to 608px, which is a soft image. The same mistake in the other direction, such as 100vw on a small slot, makes the browser download an image that is far larger than it needs.
⚠ Warning: Do not set sizes="100vw" on every image to be safe. It is the most common wrong value, and the cost shows up as megabytes on phone data plans.
Common mistakes
- Writing
sizesonce and never updating it when the grid changes. - Offering only two widths and asking the browser to cover every density.
- Using
widthdescriptors that do not match the real file widths. - Forgetting
widthandheight, so the grid jumps when each image arrives.
Related course: HTML and CSS Advanced.
About
Measure the real slot width at each breakpoint, write sizes from it, and check which file loads at each screen density.
- Responsible
- Jay P
- Last Update
- 08/26/2026
- Reading Time
- 3 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.