CSS Responsive Images
Responsive images look sharp on every screen — from a small phone to a 4K monitor — while loading only as much image data as the device actually needs. A phone does not need to download a 3000px-wide image to display it at 375px wide. Responsive image techniques solve this without sacrificing quality.
The Core CSS Rule
This is the starting point for every responsive image:
img {
max-width: 100%;
height: auto;
}
DIAGRAM — max-width: 100% behavior:
Image natural size: 1200px
On a 1440px screen: image displays at 1200px (its natural size — no upscale)
On a 900px screen: image scales to 900px (fits the container)
On a 375px screen: image scales to 375px (fits the phone screen)
Image never overflows its container, never distorts.
width: 100% vs max-width: 100%
DIAGRAM — Comparison:
img { width: 100%; }
→ Always fills 100% of the container.
A 100px image in a 1000px container stretches to 1000px — pixelated!
img { max-width: 100%; }
→ Fills the container up to the image's natural size.
A 100px image in a 1000px container stays at 100px — crisp!
Use max-width: 100% for content images. Use width: 100% only when you deliberately want the image to fill the container at all sizes, paired with object-fit: cover to avoid stretching.
The srcset Attribute — Different Resolutions
HTML's srcset attribute lets you offer multiple image files at different resolutions. The browser picks the best one for the device.
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w,
photo-800.jpg 800w,
photo-1600.jpg 1600w"
alt="Mountain view"
>
DIAGRAM — srcset decision:
Phone (375px screen, 2x pixel ratio):
Needs: 375 × 2 = 750px wide image
Browser picks: photo-800.jpg ← closest fit
Desktop (1440px screen, 1x pixel ratio):
Needs: 1440px wide image
Browser picks: photo-1600.jpg ← closest fit
Browser downloads only what it needs — no wasted data.
The sizes Attribute — Layout Hints
sizes tells the browser how wide the image will display at different breakpoints, so it picks the right file from srcset.
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw,
(max-width: 1024px) 50vw,
800px"
alt="Mountain view"
>
DIAGRAM — sizes tells the browser the image's display width:
Screen 400px wide → sizes says "100vw" = 400px display width
Screen 800px wide → sizes says "50vw" = 400px display width
Screen 1200px wide → sizes says "800px" = 800px display width
Browser combines this with srcset to pick the optimal file.
The picture Element — Art Direction
The <picture> element lets you show a completely different image crop or composition on different screen sizes — not just a different resolution of the same image.
<picture>
<source media="(max-width: 600px)" srcset="photo-portrait.jpg">
<source media="(max-width: 1024px)" srcset="photo-square.jpg">
<img src="photo-landscape.jpg" alt="Coastal town">
</picture>
DIAGRAM — Art direction with picture:
Phone (portrait):
┌──────┐
│ 📸 │ ← photo-portrait.jpg: tight vertical crop
│ crop │ (faces or key subject fills the frame)
└──────┘
Tablet (square):
┌────────┐
│ 📸📸 │ ← photo-square.jpg: medium crop
└────────┘
Desktop (landscape):
┌──────────────────────┐
│ 📸📸📸📸📸 │ ← full landscape photo
└──────────────────────┘
Modern Image Formats with picture
Use <picture> to serve modern formats (WebP, AVIF) to browsers that support them, with a JPEG fallback.
<picture>
<source type="image/avif" srcset="photo.avif">
<source type="image/webp" srcset="photo.webp">
<img src="photo.jpg" alt="Description">
</picture>
DIAGRAM — Format fallback chain:
Browser supports AVIF? → YES → use photo.avif (smallest file)
Browser supports WebP? → YES → use photo.webp (smaller than JPEG)
Fallback → use photo.jpg (universal support)
File Size Comparison (same image)
| Format | Relative File Size | Browser Support |
|---|---|---|
| JPEG | 100% (baseline) | All browsers |
| WebP | ~65% of JPEG | All modern browsers |
| AVIF | ~50% of JPEG | Chrome, Firefox, Safari (2023+) |
Responsive Background Images
For CSS background images, use @media queries to swap image sources at different breakpoints.
.hero {
background-image: url("hero-mobile.jpg");
background-size: cover;
background-position: center;
}
@media (min-width: 600px) {
.hero {
background-image: url("hero-tablet.jpg");
}
}
@media (min-width: 1024px) {
.hero {
background-image: url("hero-desktop.jpg");
}
}
High-DPI Screens (Retina) with CSS
Retina and high-DPI displays use 2x or 3x more physical pixels than standard screens. Images look blurry at 1x. Use the resolution media query to serve sharper images.
.logo {
background-image: url("logo.png");
background-size: 120px 40px;
}
@media (min-resolution: 2dppx) {
.logo {
background-image: url("logo@2x.png"); /* 2× the pixel density */
}
}
Lazy Loading
Load offscreen images only when the user scrolls near them. This is a single HTML attribute — no CSS needed.
<img src="photo.jpg" alt="Description" loading="lazy">
Combine this with a CSS placeholder color so users see a smooth colored box while the image loads:
img {
max-width: 100%;
height: auto;
background: #f0f0f0;
display: block;
}
Responsive Image Checklist
| Technique | When to Use It |
|---|---|
| max-width: 100%; height: auto | Every image on the page |
| srcset with width descriptors | Serving different sizes of the same image |
| sizes attribute | Paired with srcset for accurate selection |
| picture + source | Different crops per screen, or modern formats |
| @media background swap | CSS background images |
| loading="lazy" | All images below the fold |
| AVIF/WebP via picture | Reducing page weight for all users |
Responsive images improve page speed, reduce mobile data usage, and ensure content looks sharp on high-DPI screens. The max-width: 100% rule handles the basics; srcset, sizes, and <picture> handle the advanced optimization.
