Performance
srcset and sizes without mistakes
srcset lists the variants, sizes says how much room the image takes. Without sizes the browser fetches the largest file: syntax, mistakes and checks.
Key points
- With the w descriptor the browser picks the variant from sizes and screen density.
- Without sizes the browser assumes 100vw and takes the largest file.
- srcset does not convert formats or create variants: the files must exist.
srcset lists the variants with their width in w, sizes says how much room the image takes in the layout. Without sizes the browser assumes 100vw and fetches the largest file it has.
w, x and sizes: who decides
With the w descriptor the browser multiplies the space declared in sizes by the screen density and picks the variant that covers it. With x it picks by density and ignores the layout. The two descriptors are never mixed in the same srcset.
The mistakes that cost bytes
| Mistake | What the browser fetches | Fix |
|---|---|---|
| Missing sizes | The largest variant, always | Declare the real width of the container |
| sizes too wide | A variant larger than needed | Match sizes to the layout breakpoint |
| w and x together | The first valid variant | Use w with sizes only |
| Missing width and height | Nothing extra, but the layout moves | Add width, height and aspect-ratio |
Correct markup
width and height give the aspect ratio even when CSS overrides the sizes. If the image sits above the fold, drop loading="lazy": deferred loading on an LCP element makes the metric worse.
Check the choice, not the syntax
document.querySelectorAll("img").forEach((img) => console.log(img.currentSrc, img.naturalWidth));
In DevTools open Network, filter Img and look at which file actually arrives: if the 980 variant arrives at a 375 px viewport, the problem is sizes. currentSrc is the variant the browser chose, not the one you wrote.
The limitation you have to name
srcset does not convert formats and does not create the variants: the files must exist, at the widths you declare. AVIF or WebP need picture with source elements, and a hand-written srcset ages with the first layout change: in our catalogue the component generates it, which is why there is a single sizes string to check.
Where to continue
In the catalogue: responsive hero banner, reveal images and hero components. The guides: images with next/image and images without dimensions; the sibling article on LCP and the comparison of Next.js and Vite.
Frequently asked questions
What is the difference between srcset and sizes?
srcset lists the available variants with their width or density, sizes tells the browser how much room the image takes. The browser combines both to pick the file.
Can I use srcset without sizes?
Yes, but only with x descriptors. With w descriptors and no sizes the browser assumes the image fills the viewport width and fetches the largest variant.
Do I need width and height with srcset?
Yes, if you want to avoid layout shift. srcset picks the file, width and height reserve the space: without them the content moves when the image arrives.
Does srcset convert images to WebP?
No. srcset picks among files that already exist. Changing format needs picture with several source elements, or a service that converts on request.
How do I check which file was downloaded?
Read currentSrc in the console, or the request column in DevTools with the Img filter. If the chosen file is larger than the space available, the problem is almost always sizes.