Learn how to serve the right image for every screen size using srcset, sizes, the picture element, and modern performance attributes.
A single image file cannot be optimal for every device. A high-resolution photo perfect for a desktop monitor wastes bandwidth on a phone, while a small image looks blurry on a retina display.
Responsive images solve this by letting the browser choose the best image source based on the device's viewport width, pixel density, and supported formats.
The srcset attribute on <img> provides a set of image sources with their intrinsic widths:
<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: 1000px) 50vw, 33vw"
alt="A landscape photo"
>srcset lists available images with their width descriptors (400w, 800w, etc.).sizes tells the browser how wide the image will be displayed at each breakpoint.src is the fallback for browsers that do not support srcset.The browser combines sizes with the device pixel ratio to pick the best file. You never know exactly which image will be loaded — and that is the point. The browser optimises for the user's context.
For art direction (cropping or completely changing the image at different breakpoints) or format switching, use the <picture> element:
<picture>
<source media="(max-width: 600px)" srcset="photo-mobile.jpg">
<source media="(max-width: 1200px)" srcset="photo-tablet.jpg">
<img src="photo-desktop.jpg" alt="A landscape photo">
</picture>Serve modern formats to browsers that support them:
<picture>
<source srcset="photo.avif" type="image/avif">
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="A landscape photo">
</picture>The browser picks the first <source> it supports. The <img> is required as the fallback and is where the alt text lives.
Modern HTML provides attributes to improve image loading performance:
loading="lazy" — Defers loading until the image is near the viewport. Do not use this on images visible on initial load (above the fold).decoding="async" — Hints the browser to decode the image off the main thread.width and height — Always include these to prevent Cumulative Layout Shift (CLS). The browser reserves the correct space before the image loads.fetchpriority="high" — Hints that this image is critical (e.g., hero images). Use sparingly.<!-- Responsive image with srcset and sizes -->
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="Mountain landscape at sunset"
width="800"
height="600"
loading="lazy"
decoding="async"
>
<!-- Art direction with picture -->
<picture>
<source media="(max-width: 600px)" srcset="banner-mobile.jpg">
<source media="(max-width: 1200px)" srcset="banner-tablet.jpg">
<img src="banner-desktop.jpg" alt="Site banner" width="1200" height="400">
</picture>
<!-- Format switching -->
<picture>
<source srcset="logo.avif" type="image/avif">
<source srcset="logo.webp" type="image/webp">
<img src="logo.png" alt="Company logo" width="200" height="60">
</picture>What is the difference between using srcset on <img> and using the <picture> element?