HTML Tutorial · HTML5 Features · Lesson 22 of 34
HTML Responsive Images
Serve the right image size and format with srcset, sizes and picture, and avoid layout shifts.
The problem
A 2400-pixel photo looks great on a desktop, but a phone downloads the same megabytes to show it 400 pixels wide. Responsive images let the browser choose a suitable file.
srcset and sizes
srcsetlists the available files and their widths (w) or pixel densities (x).sizestells the browser how wide the image will be displayed at different viewport widths.- The browser picks the smallest file that still looks sharp, based on the screen and its pixel density.
<img srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
src="medium.jpg" width="1600" height="800" alt="Harbour at dusk">
<picture>
<source media="(max-width: 500px)" srcset="data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27500%27%20height%3D%27500%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23bae6fd%27%2F%3E%3Ctext%20x%3D%2720%27%20y%3D%27260%27%20font-size%3D%2730%27%3Eportrait%20crop%20%28narrow%29%3C%2Ftext%3E%3C%2Fsvg%3E">
<img src="data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%271000%27%20height%3D%27400%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fde68a%27%2F%3E%3Ctext%20x%3D%2720%27%20y%3D%27210%27%20font-size%3D%2740%27%3Ewide%20crop%20%28wide%20screens%29%3C%2Ftext%3E%3C%2Fsvg%3E"
width="1000" height="400" alt="Demo image whose crop depends on the screen width" style="max-width:100%; height:auto">
</picture>
<p id="pick"></p>
<script>
// Which file would srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1600w" with sizes="(max-width: 600px) 100vw, 50vw" pick here?
const needed = (innerWidth <= 600 ? innerWidth : innerWidth / 2) * devicePixelRatio;
const file = [[400, "small.jpg"], [800, "medium.jpg"], [1600, "large.jpg"]].find(([w]) => w >= needed) ?? [1600, "large.jpg"];
document.getElementById("pick").textContent =
`Viewport ${innerWidth}px at ${devicePixelRatio}x needs about ${Math.round(needed)}px, so srcset would choose ${file[1]}.`;
</script>
Real srcset files live on your server, so this preview shows the idea instead: the <picture> switches crops with a media query, and the script works out which srcset file your screen would get. Open the example on a phone and on a desktop to compare.
picture: different crops or formats
<picture> lets you switch sources by media query or file format. The browser uses the first <source> that matches and falls back to the <img>.
<picture>
<source type="image/avif" srcset="hero.avif">
<source type="image/webp" srcset="hero.webp">
<source media="(max-width: 600px)" srcset="hero-portrait.jpg">
<img src="hero.jpg" alt="Hikers on a ridge at sunrise" width="1200" height="600">
</picture>
Avoid layout shift and slow loading
- Always set
widthandheight(the image's real proportions). The browser reserves space before the file arrives, so text doesn't jump. - Add
loading="lazy"to images below the fold, but not to the main hero image, which should load as early as possible (optionally withfetchpriority="high"). - Write a useful
alttext, oralt=""for purely decorative images.
<p>The image below reserves its space before loading, so this text won't jump.</p>
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='300'%3E%3Crect width='100%25' height='100%25' fill='%23c7d2fe'/%3E%3C/svg%3E"
width="600" height="300" loading="lazy" alt="Placeholder landscape" style="max-width:100%; height:auto">
<p>Text after the image.</p>
Test yourself
Why should you set width and height on images even with CSS sizing?
To make the file download fasterSo the browser reserves the right space before the file loads and the layout doesn't shiftBecause srcset requires itTo disable lazy loading
Show answer
Answer: So the browser reserves the right space before the file loads and the layout doesn't shift
With the aspect ratio known up front, text below the image doesn't jump when it loads (better Cumulative Layout Shift).