Images Srcset and Sizes in HTML
Learn how to use srcset and sizes attributes for responsive images in HTML. Covers width descriptors, pixel density, device pixel ratio and more.
You’ve already learned how to add images to a webpage. You know how to write a proper <img> tag, pick the right format, and even write good alt text. That’s solid progress.
But here’s something most beginners don’t think about: that beautiful 1800px wide hero image you’re serving? Your mobile visitors are downloading the full thing too. All of it. Every single heavy byte, even though their phone screen is only 390px wide.
That’s where responsive images in HTML come in.
This tutorial covers exactly how to serve the right image to the right screen, using the srcset and sizes attributes. By the end, you’ll understand how to stop wasting bandwidth and make your pages load noticeably faster, especially on mobile.
Let’s get into it.
01Why Your Images Are Probably Too Heavy Right Now
Think about a standard blog post. It has a banner image, maybe a few inline images. Each one is probably exported at a fixed size, say 1200px wide, and then stuck into an <img> tag with a max-width: 100% in your CSS.
That works visually. The image scales down on mobile. But the browser still downloaded the full 1200px version. On a phone with a slow connection, that’s a real problem.
According to HTTP Archive data, images are consistently the largest assets on most web pages. They’re the single biggest contributor to slow page loads. And a big chunk of that problem is avoidable with a few lines of HTML.
The solution is responsive images in HTML: giving the browser multiple image options and letting it pick the best one based on the device, screen size, and connection.
02What the srcset Attribute Actually Does
The srcset attribute (html image srcset, to be precise) is how you offer the browser a menu of image files. Instead of one image, you give it two, three, or four options and let it choose the most appropriate one.
Here’s the simplest possible example:
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
alt="A mountain landscape"
>
The src is still there. That’s your fallback for browsers that don’t support srcset (though honestly, support is near-universal at this point). The srcset is the list of options.
Each entry has two parts:
- The image file path
- A width descriptor (the
wvalue)
That 400w means “this image is 400 pixels wide.” You’re telling the browser the actual intrinsic width of each image file. The browser then uses that information, combined with the device screen width and pixel density, to pick the right one.
Notice you’re not making that decision yourself. The browser decides. That’s intentional and that’s actually the smart part of this whole system.
03Width Descriptors vs Pixel Density Descriptors
There are two ways to write srcset values. Width descriptors use w, and pixel density descriptors use x. They solve slightly different problems.
Width Descriptors (w): For Layout-Dependent Images
Width descriptors are the more powerful option. You list the actual pixel width of each image file, and the browser figures out which one fits best given the viewport and the display density.
<img
src="banner-800.jpg"
srcset="
banner-400.jpg 400w,
banner-800.jpg 800w,
banner-1600.jpg 1600w
"
alt="Website banner image"
>
This is what you’ll use most of the time for responsive images in HTML when images change size based on the layout.
Pixel Density Descriptors (x): For Fixed-Size Images
Pixel density descriptors are simpler. You use 1x, 2x, or 3x to describe the display density you’re targeting. This is great for things like logos, icons, or any image that appears at a fixed size but needs to look sharp on high-DPI screens.
<!-- Logo displayed at 200px wide, sharp on all screens -->
<img
src="logo.png"
srcset="logo.png 1x, [email protected] 2x, [email protected] 3x"
width="200"
height="60"
alt="Company logo"
>
On a standard 1x screen, the browser picks logo.png. On a Retina or high-DPI screen (which has a device pixel ratio of 2 or 3), it picks the sharper version automatically.
Srcset Syntax Breakdown
image file path
width descriptor
image file path
width descriptor
image file path
width descriptor
File path: the actual image URL
Width descriptor: the real pixel width of that file
04Device Pixel Ratio: The Hidden Factor You Need to Understand
Here’s where things get a bit more interesting. A modern phone might have a screen that’s 390px wide in CSS terms, but it actually has 1170 physical pixels across. That ratio (1170 ÷ 390 = 3) is called the device pixel ratio, or DPR.
A device pixel ratio of 2 means every 1 CSS pixel uses 2 physical pixels. A DPR of 3 means 3 physical pixels per CSS pixel. That’s why images look blurry on Retina screens if you only serve a low-res version: there aren’t enough pixels in the image to fill all those physical pixels on the display.
This matters a lot for responsive images in HTML because the browser uses the device pixel ratio when deciding which image from your srcset to pick. On a 390px wide screen with a DPR of 3, the browser knows it needs roughly a 1170px-wide image to look crisp. So it’ll reach for the larger image in your srcset list.
Device Pixel Ratio Explained
The key insight: the CSS layout width is NOT the number of pixels the image needs. You have to multiply it by the device pixel ratio to get the actual pixel requirement. This is exactly what the browser does when it reads your srcset list, and it’s why just guessing “I’ll serve a big image” doesn’t cut it anymore.
05The sizes Attribute: The Missing Piece Most Tutorials Skip
Here’s where a lot of tutorials stop, and also where a lot of developers end up with images that still aren’t quite right. The srcset alone tells the browser “here are your options.” But the image sizes attribute tells the browser “and here’s how wide this image will actually appear on the page.”
Without sizes, the browser assumes the image will be 100% of the viewport width. That’s almost never true. Your layout probably has padding, a sidebar, a container with a max-width. The browser doesn’t know any of that from HTML alone because CSS hasn’t been parsed yet when it starts downloading images.
The sizes attribute fills that gap.
How the sizes Attribute Works
The image sizes attribute is a list of conditions and sizes, like this:
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 33vw"
alt="A responsive photo"
>
Read it left to right. The browser checks each condition in order:
- If the viewport is 600px or narrower, the image takes up 100% of the viewport width (
100vw) - If the viewport is 1024px or narrower, the image takes up 50% (
50vw) - Otherwise (bigger screens), the image takes up 33% (
33vw)
These match your CSS layout. If your image is full-width on mobile, half-width on tablets, and one-third on desktop, that’s exactly what you write in sizes.
Armed with that info, the browser does the math: viewport width × image display size = pixels needed. Then it picks the smallest image from your srcset that still covers that requirement. It’s genuinely smart.
How the Browser Picks an Image
06Putting srcset and sizes Together: A Real Example
Let’s build something concrete. Say you have a blog post image. On mobile (under 640px), it takes up the full width. On tablets (640px to 1024px), it takes up 70% of the page. On desktop, it sits in a container and takes up about 640px.
Here’s how that looks in practice:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Image Example</title>
<style>
.post-image {
width: 100%;
height: auto;
border-radius: 12px;
display: block;
}
.container {
max-width: 960px;
margin: 0 auto;
padding: 1rem;
}
</style>
</head>
<body>
<div class="container">
<img
class="post-image"
src="https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png"
srcset="
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 400w,
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 800w,
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 1200w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 1024px) 70vw,
640px
"
width="1200"
height="630"
alt="A responsive blog post image demonstrating srcset and sizes"
>
</div>
</body>
</html>
A few things to notice in that example:
- The
widthandheightattributes are still there. Always set them. They prevent layout shift while the image loads. - The last value in
sizeshas no condition. That’s the default fallback, used when none of the earlier conditions match. - The
srcis still present as a fallback. Old browsers that can’t readsrcsetwill use it.
This is the full picture of responsive images in HTML: srcset gives the browser the menu, sizes tells it the context, and the browser makes the final call.
07The sizes Value Can Also Be a Fixed Pixel Value
You’re not stuck using vw units. If you know an image will always appear at a fixed size in your layout, say inside a 640px wide card component, you can just write 640px directly.
<!-- Image always renders at 640px wide no matter what -->
<img
src="card-photo.jpg"
srcset="card-photo-320.jpg 320w, card-photo-640.jpg 640w, card-photo-1280.jpg 1280w"
sizes="640px"
alt="Card image"
>
<!-- Image is full width on mobile, fixed 480px on larger screens -->
<img
src="profile.jpg"
srcset="profile-300.jpg 300w, profile-600.jpg 600w, profile-900.jpg 900w"
sizes="(max-width: 600px) 100vw, 480px"
alt="Profile photo"
>
Mixing fixed pixel values and viewport-relative values is totally fine. Use whatever accurately describes how your layout behaves.
08Width Descriptors vs Pixel Density Descriptors: Which One to Use
This trips people up, so let’s settle it clearly.
Use width descriptors (w) when the image size changes based on the layout. This is most images on most pages, including hero images, article images, cards, product photos, and anything that resizes with the viewport.
Use pixel density descriptors (x) when the image is always displayed at a fixed CSS size but you want it to look sharp on high-DPI screens. This is best for logos, icons, buttons with background images, and small UI elements.
w vs x: Quick Reference
| Feature | Width Descriptor w | Pixel Density x |
|---|---|---|
| Best for | Layout-fluid images | Fixed-size images |
| Requires sizes? | ✓ Yes, recommended | No (not needed) |
| Considers DPR? | ✓ Yes, automatically | ✓ Yes, directly |
| Typical use | Hero images, articles, cards | Logos, icons, UI elements |
| Example | photo.jpg 800w |
[email protected] 2x |
A quick personal note: I’d say 90% of your use cases will be width descriptors with sizes. Get that pattern down first, then add pixel density handling for your fixed UI elements.
09Common Mistakes People Make with Responsive Images in HTML
Let’s talk about the things that trip people up when they start working with responsive images in HTML. I’ve seen these mistakes come up again and again.
Mistake 1: Using srcset Without sizes
Adding srcset but skipping sizes means the browser assumes the image is 100vw wide. On a desktop with a narrow content column, the browser might unnecessarily grab a much larger image than needed.
<!-- Not ideal: browser assumes 100vw -->
<img src="photo.jpg" srcset="photo-400.jpg 400w, photo-800.jpg 800w">
<!-- Better: tell the browser the actual display width -->
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="(max-width: 640px) 100vw, 50vw"
alt="Description of photo"
>
Mistake 2: Putting Lies in the Width Descriptor
The w value must match the actual pixel width of the image file. If your file is 800px wide, write 800w, not 1000w. The browser trusts these numbers completely. Wrong numbers lead to the wrong image being selected.
<!-- Wrong: lying about the image width -->
<img srcset="photo-800.jpg 1200w">
<!-- photo-800.jpg is actually 800px wide, not 1200px -->
<!-- Correct: match the descriptor to the real file width -->
<img srcset="photo-800.jpg 800w">
Mistake 3: Forgetting the alt Attribute
If you’ve read our guide on alt text for images, you already know this. Responsive images in HTML are still images. They need descriptive alt text. Every time.
Mistake 4: Skipping width and height on the img Tag
If you don’t set width and height attributes on your <img>, the browser doesn’t know how much space to reserve before the image loads. This causes layout shift, which hurts your Core Web Vitals score and feels jarring for users. Always set them. We covered this in detail in our images in HTML5 guide.
Mistake 5: Not Actually Generating the Different Image Sizes
This one sounds obvious but you’d be surprised. Adding the HTML is just one part. You actually need to create and export the multiple image files at the different sizes. Whether you use an image editor, a build tool, or a CDN with image resizing, the files need to exist at the paths you reference.
10How the Browser Makes Its Decision (and Why You Can’t Control It)
Here’s something worth understanding about responsive images in HTML: the browser is in charge. Not you.
You provide the menu (srcset) and the hint (sizes). The browser decides which image to actually download. It might factor in network speed, the user’s data-saving preferences, the device’s memory, or other heuristics. It could pick a different image than you expect.
That’s by design. The browser has more context than your HTML does. And honestly, that’s a good thing: it means on a slow connection, the browser might pick a smaller image even if a larger one would technically fit the viewport. That’s the kind of intelligent behavior that makes responsive images in HTML genuinely powerful.
If you need artistic control, like showing a completely different crop of an image on mobile vs desktop, that’s where the <picture> element comes in. We’ll cover that in the next article: Understanding the Picture Element in HTML. It gives you precise control over which image loads in which context.
11A Complete Responsive Images in HTML Example to Study
Let’s write a full, clean example that covers everything we’ve talked about:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Images Complete Example</title>
<style>
body {
font-family: sans-serif;
margin: 0;
padding: 1rem;
background: #111;
color: #eee;
}
.container {
max-width: 900px;
margin: 0 auto;
}
/* 1. A fluid image (width changes with layout) */
.hero-image {
width: 100%;
height: auto;
border-radius: 12px;
display: block;
margin-bottom: 2rem;
}
/* 2. A fixed-size image (always 200px wide) */
.logo {
width: 200px;
height: auto;
display: block;
margin-bottom: 2rem;
}
/* 3. A card grid image */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.card img {
width: 100%;
height: 160px;
object-fit: cover;
border-radius: 8px;
display: block;
}
</style>
</head>
<body>
<div class="container">
<!-- 1. Hero image: fluid, sizes matches CSS layout -->
<img
class="hero-image"
src="https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png"
srcset="
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 400w,
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 800w,
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 1200w
"
sizes="(max-width: 640px) 100vw, 900px"
width="1200"
height="630"
alt="Hero image showing a sample web page"
>
<!-- 2. Logo: fixed size, use pixel density for sharpness -->
<img
class="logo"
src="https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png"
srcset="
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 1x,
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 2x
"
width="200"
height="60"
alt="Site logo"
>
<!-- 3. Card grid: images at about 1/3 viewport on desktop, full on mobile -->
<div class="card-grid">
<div class="card">
<img
src="https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png"
srcset="
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 400w,
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 800w
"
sizes="(max-width: 640px) 100vw, 33vw"
width="400"
height="300"
alt="Card image one"
>
</div>
<div class="card">
<img
src="https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png"
srcset="
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 400w,
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 800w
"
sizes="(max-width: 640px) 100vw, 33vw"
width="400"
height="300"
alt="Card image two"
>
</div>
<div class="card">
<img
src="https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png"
srcset="
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 400w,
https://daniyaldev.com/wp-content/uploads/2026/01/social-share-scaled.png 800w
"
sizes="(max-width: 640px) 100vw, 33vw"
width="400"
height="300"
alt="Card image three"
>
</div>
</div>
</div>
</body>
</html>
Three different patterns in one page: a fluid hero image, a fixed-size logo with density descriptors, and a card grid with layout-aware srcset. This covers the vast majority of real-world scenarios.
12Performance Benefits You Get from Responsive Images in HTML
Let’s be specific about what you’re actually gaining here, because the wins are real.
A 1600px JPEG image might weigh 400KB. The 400px version of the same image might be 40KB. If a mobile user in a developing country on a 3G connection visits your page, that’s the difference between a 1-second load and a 10-second wait. Not an exaggeration.
Implementing responsive images in HTML properly has a direct effect on your Core Web Vitals, specifically Largest Contentful Paint (LCP). LCP measures how fast the biggest visible element on the page loads. For most pages, that’s a hero image. Cut its size by 80% on mobile and your LCP score improves dramatically.
It also reduces bandwidth costs. For users on metered connections, serving a 400px image instead of a 1600px image is a real act of consideration. Your visitors notice it, even if they don’t know why the page feels fast.
Image File Size: Mobile Device Comparison
That’s not a trick or an edge case. That’s what responsive images in HTML does for your pages every day, for every mobile visitor.
13Browser Support For srcset and sizes
The srcset and sizes attributes have excellent browser support across all modern browsers, Chrome, Firefox, Safari, Edge, and mobile browsers. Support has been near-universal since around 2016.
For the rare case where someone is on a very old browser, your src fallback handles it gracefully. There’s no downside to adding srcset today in 2026.
You can check the latest exact support data at caniuse.com/srcset if you need specifics for a particular project requirement.
14Quick Reference: Responsive Images in HTML Cheat Sheet
Responsive Images Cheat Sheet
img-400.jpg 400w,
img-800.jpg 800w,
img-1200.jpg 1200w
“
(max-width: 640px) 100vw,
(max-width: 1024px) 50vw,
33vw
“
width=”800″
height=”600″
alt=”Description”
15What’s Next?
Everything you’ve learned here handles one specific job really well: giving the browser a choice of image sizes so it can pick the most efficient one. That covers the majority of cases for responsive images in HTML.
But sometimes you need more control. What if you want a completely different crop of an image on mobile? Or you want to serve a WebP image to browsers that support it, and a JPEG to those that don’t? What if the image composition needs to change, not just the file size?
That’s where the <picture> element comes in. It’s the next step up from srcset and sizes, and it gives you full art direction control. We’ll dig into that in the next article.
For now, you have everything you need to start implementing responsive images in HTML today. The srcset and sizes pattern is one of the most impactful small changes you can make to a real website’s performance.
Images Srcset and Sizes in HTML
Loading challenge…
Ready to Practice?
Select a difficulty level above to load your challenge.