Responsive Images Explained: srcset, sizes, and Lazy Loading
Learn how modern web developers serve the perfect image to every device using srcset, sizes attributes, and lazy loading — without breaking a sweat.

Here's the thing: your beautiful 3000×2000 hero image looks amazing on your 27-inch monitor. But someone just downloaded 4.2MB of pixels on their phone to see a 375-pixel-wide thumbnail. Their data plan weeps.
This is why responsive images exist. Not because web standards committees enjoy making HTML complicated (though they do seem to have fun with it), but because serving one image size to every device is wasteful, slow, and kind of rude to mobile users.
Why the Old Way Doesn't Work
Back in the day, you'd slap an image on your page with a simple <img src="photo.jpg"> and call it done. CSS would scale it down for smaller screens with max-width: 100%, and everyone was happy.
Except the browser was still downloading the full-size file. A 2400px-wide image displayed at 375px? Still downloading 2400px worth of bytes.
That's like ordering a large pizza when you only want one slice, except the pizza costs bandwidth and the hunger is your impatient users watching a loading spinner.
Enter srcset: The Image Buffet
The srcset attribute is your way of telling the browser, "Hey, I've got this image in multiple sizes. Pick whichever makes sense."
Here's what it looks like:
<img
src="product-800w.jpg"
srcset="
product-400w.jpg 400w,
product-800w.jpg 800w,
product-1200w.jpg 1200w,
product-1600w.jpg 1600w
"
alt="Product photo"
/>That 400w, 800w notation? That's telling the browser the actual pixel width of each file. Not the display size — the file size.
The browser looks at the screen size, pixel density (hello, Retina displays), and network conditions, then picks the best match. You don't have to think about it.
And if you're wondering how to quickly generate those different sizes, tools like KokoConvert's Image Resizer make it painless. Upload once, export at multiple widths, done.
The sizes Attribute: Giving the Browser Context
So srcset tells the browser what images exist. But it still needs to know how much space the image will actually take up on the page.
That's where sizes comes in.
<img
src="product-800w.jpg"
srcset="
product-400w.jpg 400w,
product-800w.jpg 800w,
product-1200w.jpg 1200w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 1024px) 50vw,
33vw
"
alt="Product photo"
/>Translation:
- On screens up to 640px wide, the image takes up 100% of the viewport width
- On screens up to 1024px, it's 50%
- On larger screens, it's 33%
Now the browser has the full picture (pun intended). It knows what files exist, and it knows how much screen space the image occupies. It can do the math and grab the right file.
And honestly? This is where a lot of developers get confused. The sizes attribute isn't telling the browser what size to display the image — CSS still does that. It's just giving the browser a heads-up so it can make a smart download decision before CSS even loads.
Lazy Loading: Don't Load What You Can't See
Alright, so you've got responsive images sorted. But here's the next problem: your blog post has 15 images. Do you really need to download all 15 the moment the page loads, even though the user can only see the first two?
Nope.
That's where lazy loading comes in. Add one attribute:
<img src="photo.jpg" loading="lazy" alt="..." />And boom — the browser only downloads images as they're about to scroll into view. It's native, it's fast, and it's supported everywhere that matters.
One caveat: don't lazy-load your hero image. Anything above the fold (visible immediately when the page loads) should load normally. Lazy loading is for stuff further down the page.
Also, if you've got a massive gallery or portfolio, consider using image compression on top of lazy loading. Smaller files + deferred loading = very happy users.
Modern Formats: WebP and AVIF
While we're talking about optimizing images, let's not ignore format. JPEG and PNG have been around forever, but newer formats like WebP and AVIF offer way better compression.
WebP can reduce file sizes by 25-35% compared to JPEG at the same visual quality. AVIF goes even further — sometimes 50% smaller.
You can serve modern formats with fallbacks using the <picture> element:
<picture>
<source srcset="photo.avif" type="image/avif" />
<source srcset="photo.webp" type="image/webp" />
<img src="photo.jpg" alt="Fallback for old browsers" />
</picture>Browsers try AVIF first, fall back to WebP if that's not supported, and finally load the JPEG if neither works. It's like a priority queue for image formats.
Need to convert a batch of images to WebP or AVIF? KokoConvert handles that without making you install anything.
Real-World Workflow
So what does this look like in practice? Here's how most devs handle it:
- Start with high-res originals — at least 2x the largest display size you need
- Generate multiple sizes — typically 400w, 800w, 1200w, 1600w, and 2400w cover most use cases
- Convert to modern formats — WebP at minimum, AVIF if you're feeling fancy
- Use srcset + sizes for automatic selection
- Add loading="lazy" to everything except above-the-fold images
If you're using a framework like Next.js, a lot of this is automated with the <Image> component. But even if you're hand-coding HTML, it's not that hard once you get the pattern down.
Common Mistakes
Using too many breakpoints. You don't need 20 different image sizes. Start with 4-5 and call it a day. Diminishing returns kick in fast.
Lazy-loading everything. Again: hero images, logos, and anything immediately visible should not be lazy-loaded. You'll just delay the initial render for no reason.
Forgetting alt text. Responsive images are great, but if you skip alt text, you've just made your site inaccessible to screen readers. Don't do that.
Not testing on real devices. Emulators are fine for layout, but actually open your site on a phone with a slow connection. You'll learn real quick if your images are bloated.
The Bottom Line
Responsive images used to feel like overkill. Now they're baseline. If your site serves the same giant image to every device, you're wasting bandwidth, slowing down page loads, and annoying mobile users who are already dealing with spotty LTE.
srcset and sizes give browsers the info they need to make smart choices. loading="lazy" keeps initial page weight down. Modern formats like WebP and AVIF shrink file sizes without sacrificing quality.
Put it all together, and you've got images that look great everywhere, load fast, and don't burn through data caps.
That's the goal. And honestly? It's not even that hard once you've done it a few times.
Frequently Asked Questions
What's the difference between srcset and sizes?
srcset defines which image files are available and their widths (e.g., photo-800w.jpg 800w), while sizes tells the browser how much screen space the image will occupy (e.g., 50vw for half the viewport). The browser uses both to pick the optimal file.Does lazy loading affect SEO?
loading="lazy". Just don't lazy-load your hero image or anything above the fold — those should load immediately for both users and bots.