Responsive Images Tutorial
Make your images look perfect on every device! Learn how to serve the right image size for phones, tablets, and desktops using modern HTML responsive image techniques.
Why Responsive Images Matter
Loading a 3000px wide image on a 375px phone screen wastes bandwidth, slows page loads, and drains battery life. Responsive images solve this by serving appropriately sized images based on the user’s device.
Benefits of responsive images:
- Faster page loads — Smaller files on mobile
- Better user experience — Optimized for each device
- Reduced bandwidth costs — Less data transfer
- Improved SEO — Google rewards fast-loading pages
The Problem with Fixed Images
Here’s what happens with traditional img tags:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fixed Image Problem</title>
</head>
<body>
<!-- Problematic example: this loads the same huge image on all devices -->
<img
src="hero-image-3000px.jpg"
alt="Beautiful landscape"
style="width: 100%; height: auto;">
<!-- A 3000px image might be 2MB, but on a phone
you only need 400px which could be 100KB! -->
</body>
</html>The browser downloads the full 2MB image even on a phone that only displays it at 400px wide. That’s wasteful!
Solution 1: The srcset Attribute
The srcset attribute lets you provide multiple image sizes and let the browser choose:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>srcset Example</title>
</head>
<body>
<img
src="landscape-800w.jpg"
srcset="landscape-400w.jpg 400w,
landscape-800w.jpg 800w,
landscape-1200w.jpg 1200w,
landscape-2000w.jpg 2000w"
alt="Beautiful mountain landscape"
style="width: 100%; height: auto;">
<!--
Browser picks the best image based on:
- Screen width
- Device pixel ratio
- Network speed (sometimes)
-->
</body>
</html>How srcset works:
400w— Image is 400 pixels wide800w— Image is 800 pixels widesrc— Fallback for old browsers- Browser automatically selects the most appropriate size
Using sizes Attribute for Better Control
The sizes attribute tells the browser how wide the image will be displayed:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>srcset with sizes</title>
<style>
.hero-image {
width: 100%;
height: auto;
}
@media (min-width: 768px) {
.hero-image {
width: 50%;
float: left;
margin-right: 20px;
}
}
</style>
</head>
<body>
<article>
<img
src="product-800w.jpg"
srcset="product-400w.jpg 400w,
product-800w.jpg 800w,
product-1200w.jpg 1200w"
sizes="(min-width: 768px) 50vw,
100vw"
alt="Premium headphones"
class="hero-image">
<h1>Premium Wireless Headphones</h1>
<p>Experience audio like never before with our latest model...</p>
</article>
<!--
sizes explanation:
- On screens 768px+: image takes 50% viewport width (50vw)
- On smaller screens: image takes full viewport width (100vw)
- Browser picks the smallest image that fits
-->
</body>
</html>sizes syntax:
- Media query condition
- Image width for that condition
- Browser evaluates top to bottom
- Last value is the default
The <picture> Element for Art Direction
Use <picture> when you need different images for different screen sizes (art direction):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Picture Element Example</title>
</head>
<body>
<picture>
<!-- Large screens: wide landscape photo -->
<source
media="(min-width: 1200px)"
srcset="hero-wide-1600w.jpg 1600w,
hero-wide-2400w.jpg 2400w">
<!-- Medium screens: standard landscape -->
<source
media="(min-width: 768px)"
srcset="hero-medium-1000w.jpg 1000w,
hero-medium-1400w.jpg 1400w">
<!-- Small screens: portrait crop -->
<source
media="(max-width: 767px)"
srcset="hero-mobile-600w.jpg 600w,
hero-mobile-800w.jpg 800w">
<!-- Fallback for browsers that don't support picture -->
<img
src="hero-medium-1000w.jpg"
alt="Modern office workspace"
style="width: 100%; height: auto;">
</picture>
<!--
Perfect for:
- Different crops for different screens
- Showing different content (portrait vs landscape)
- Highlighting different subjects
-->
</body>
</html>Key differences:
srcset— Same image, different sizes<picture>— Different images for different contexts
Modern Image Formats with <picture>
Serve WebP for modern browsers, with JPEG fallback:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modern Image Formats</title>
</head>
<body>
<picture>
<!-- Try AVIF first (best compression, newest) -->
<source
type="image/avif"
srcset="product-400.avif 400w,
product-800.avif 800w,
product-1200.avif 1200w"
sizes="(min-width: 768px) 50vw, 100vw">
<!-- Try WebP next (great compression, wide support) -->
<source
type="image/webp"
srcset="product-400.webp 400w,
product-800.webp 800w,
product-1200.webp 1200w"
sizes="(min-width: 768px) 50vw, 100vw">
<!-- JPEG fallback for older browsers -->
<img
src="product-800.jpg"
srcset="product-400.jpg 400w,
product-800.jpg 800w,
product-1200.jpg 1200w"
sizes="(min-width: 768px) 50vw, 100vw"
alt="Professional camera"
style="width: 100%; height: auto;">
</picture>
<!--
File size comparison example:
- JPEG: 200 KB
- WebP: 120 KB (40% smaller!)
- AVIF: 80 KB (60% smaller!)
-->
</body>
</html>Modern formats can be 30-60% smaller than JPEG with the same visual quality!
Responsive Image Gallery
Create a responsive photo gallery using CSS Grid and srcset:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Image Gallery</title>
<style>
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
padding: 20px;
}
.gallery-item {
position: relative;
overflow: hidden;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
.gallery-item:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0,0,0,0.2);
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.gallery-caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.7);
color: white;
padding: 15px;
transform: translateY(100%);
transition: transform 0.3s;
}
.gallery-item:hover .gallery-caption {
transform: translateY(0);
}
</style>
</head>
<body>
<div class="gallery">
<div class="gallery-item">
<img
src="photo1-600w.jpg"
srcset="photo1-400w.jpg 400w,
photo1-600w.jpg 600w,
photo1-800w.jpg 800w"
sizes="(min-width: 1200px) 25vw,
(min-width: 768px) 33vw,
(min-width: 500px) 50vw,
100vw"
alt="Sunset over mountains"
loading="lazy">
<div class="gallery-caption">
<h3>Mountain Sunset</h3>
<p>Rocky Mountains, Colorado</p>
</div>
</div>
<div class="gallery-item">
<img
src="photo2-600w.jpg"
srcset="photo2-400w.jpg 400w,
photo2-600w.jpg 600w,
photo2-800w.jpg 800w"
sizes="(min-width: 1200px) 25vw,
(min-width: 768px) 33vw,
(min-width: 500px) 50vw,
100vw"
alt="Ocean waves crashing"
loading="lazy">
<div class="gallery-caption">
<h3>Pacific Waves</h3>
<p>Big Sur, California</p>
</div>
</div>
<div class="gallery-item">
<img
src="photo3-600w.jpg"
srcset="photo3-400w.jpg 400w,
photo3-600w.jpg 600w,
photo3-800w.jpg 800w"
sizes="(min-width: 1200px) 25vw,
(min-width: 768px) 33vw,
(min-width: 500px) 50vw,
100vw"
alt="Forest with morning fog"
loading="lazy">
<div class="gallery-caption">
<h3>Misty Forest</h3>
<p>Redwood National Park</p>
</div>
</div>
<!-- Add more gallery items... -->
</div>
</body>
</html>Notice the loading="lazy" attribute — it delays loading images until they’re about to enter the viewport, improving initial page load!
Lazy Loading Images
Use native lazy loading to improve performance:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lazy Loading Images</title>
<style>
img {
width: 100%;
height: auto;
margin-bottom: 20px;
}
</style>
</head>
<body>
<!-- Images above the fold: load immediately -->
<img
src="hero-1200w.jpg"
srcset="hero-600w.jpg 600w,
hero-1200w.jpg 1200w,
hero-1800w.jpg 1800w"
sizes="100vw"
alt="Hero image"
loading="eager">
<!-- Images below the fold: lazy load -->
<img
src="content1-800w.jpg"
srcset="content1-400w.jpg 400w,
content1-800w.jpg 800w"
sizes="(min-width: 768px) 50vw, 100vw"
alt="Content image 1"
loading="lazy">
<img
src="content2-800w.jpg"
srcset="content2-400w.jpg 400w,
content2-800w.jpg 800w"
sizes="(min-width: 768px) 50vw, 100vw"
alt="Content image 2"
loading="lazy">
<!--
loading attribute values:
- "eager": Load immediately (default)
- "lazy": Load when near viewport
Use eager for critical above-the-fold images
Use lazy for everything else
-->
</body>
</html>Lazy loading tips:
- Use on images below the fold
- Don’t use on hero images or logos
- Improves Core Web Vitals scores
- Supported by all modern browsers
High DPI / Retina Displays
Serve higher resolution images for high-density displays:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Retina Images</title>
</head>
<body>
<!-- Method 1: Using srcset with x descriptor -->
<img
src="logo.png"
srcset="logo.png 1x,
[email protected] 2x,
[email protected] 3x"
alt="Company Logo"
width="200"
height="80">
<!--
x descriptor:
- 1x = Standard DPI (96 dpi)
- 2x = High DPI / Retina (192 dpi)
- 3x = Ultra high DPI (288 dpi)
-->
<!-- Method 2: Combining width and pixel density -->
<img
src="icon-100w.png"
srcset="icon-100w.png 100w,
icon-200w.png 200w,
icon-400w.png 400w"
sizes="100px"
alt="App Icon">
<!--
Browser automatically picks:
- icon-100w.png on 1x displays
- icon-200w.png on 2x displays (Retina)
- icon-400w.png on 3x displays
-->
</body>
</html>Aspect Ratio and Layout Shifts
Prevent layout shifts by reserving space for images:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aspect Ratio Images</title>
<style>
/* Method 1: Using aspect-ratio property */
.image-container-1 img {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
/* Method 2: Using padding-bottom trick */
.image-container-2 {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 = 9/16 = 0.5625 */
overflow: hidden;
}
.image-container-2 img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
</head>
<body>
<!-- Modern browsers: aspect-ratio -->
<div class="image-container-1">
<img
src="landscape-800w.jpg"
srcset="landscape-400w.jpg 400w,
landscape-800w.jpg 800w,
landscape-1200w.jpg 1200w"
sizes="100vw"
alt="Mountain landscape"
loading="lazy">
</div>
<!-- Older browser support: padding-bottom -->
<div class="image-container-2">
<img
src="landscape-800w.jpg"
srcset="landscape-400w.jpg 400w,
landscape-800w.jpg 800w,
landscape-1200w.jpg 1200w"
sizes="100vw"
alt="Mountain landscape"
loading="lazy">
</div>
<!-- Best practice: Specify width and height -->
<img
src="product-600w.jpg"
srcset="product-400w.jpg 400w,
product-600w.jpg 600w,
product-900w.jpg 900w"
sizes="(min-width: 768px) 50vw, 100vw"
alt="Product photo"
width="600"
height="400"
loading="lazy">
<!--
width/height preserves aspect ratio even if CSS changes size
Prevents Cumulative Layout Shift (CLS)
-->
</body>
</html>Common Mistakes to Avoid
Mistake 1: Not Including Fallback
Problematic example:
<img srcset="photo-400w.jpg 400w, photo-800w.jpg 800w">Improved example:
<img
src="photo-800w.jpg"
srcset="photo-400w.jpg 400w, photo-800w.jpg 800w"
alt="Description">Why: Older browsers need the src attribute as fallback.
Mistake 2: Wrong sizes Syntax
Problematic example:
<img
srcset="photo-400w.jpg 400w, photo-800w.jpg 800w"
sizes="400px, 800px">Improved example:
<img
srcset="photo-400w.jpg 400w, photo-800w.jpg 800w"
sizes="(min-width: 768px) 50vw, 100vw">Why: sizes needs media queries, not just numbers.
Mistake 3: Lazy Loading Everything
Problematic example:
<img src="hero.jpg" loading="lazy" alt="Hero">Improved example:
<img src="hero.jpg" loading="eager" alt="Hero">
<img src="content.jpg" loading="lazy" alt="Content">Why: Don’t lazy load above-the-fold images—it slows down initial render!
Mistake 4: Missing Alt Text
Problematic example:
<img src="product.jpg" srcset="...">Improved example:
<img src="product.jpg" srcset="..." alt="Blue running shoes">Why: Alt text is required for accessibility and SEO.
Mistake 5: Not Optimizing Source Images
Problematic example: Using 5MB uncompressed images
Improved example: Compress images before creating responsive versions (use tools like ImageOptim, Squoosh, or Sharp)
Why: Even with responsive images, you still need to optimize each version!
Try It Yourself
Ready to practice responsive images? Try these challenges:
Challenge 1: Basic Responsive Image (Beginner)
Create a page with:
- One hero image using srcset
- Three image sizes (400w, 800w, 1200w)
- Proper alt text
- Fallback for older browsers
Challenge 2: Responsive Gallery (Intermediate)
Build a photo gallery with:
- CSS Grid layout (3 columns on desktop, 2 on tablet, 1 on mobile)
- srcset and sizes on all images
- Lazy loading for below-the-fold images
- Hover effects on images
Challenge 3: Advanced Image Optimization (Advanced)
Create a complete responsive image solution:
<picture>element with different crops for different screens- Multiple format support (AVIF, WebP, JPEG)
- Art direction (portrait images on mobile, landscape on desktop)
- Lazy loading with aspect ratio preservation
- High DPI support for logos
Bonus: Use a build tool or image CDN (like Cloudinary or Imgix) to automatically generate multiple sizes and formats!
What You Learned
Congratulations! You now know how to:
- Use srcset for multiple image sizes
- Control image selection with sizes attribute
- Implement art direction with
<picture> - Serve modern formats (WebP, AVIF)
- Lazy load images for better performance
- Support high DPI/Retina displays
- Prevent layout shifts with aspect ratios
- Create responsive image galleries
- Optimize images for all devices
- Avoid common responsive image mistakes
Next Steps
Now that you master responsive images, explore these related tutorials:
- Adding Images — HTML image fundamentals
- Audio and Video — Responsive multimedia
- SEO Optimization — Image optimization for search
Ready to implement responsive images? Start experimenting in our interactive HTML editor!