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

  • srcset lists the available files and their widths (w) or pixel densities (x).
  • sizes tells 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 switches the crop; the script shows what srcset would pickTry it yourself
<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 width and height (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 with fetchpriority="high").
  • Write a useful alt text, or alt="" for purely decorative images.
width, height and lazy loadingTry it yourself
<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?

  1. To make the file download faster
  2. So the browser reserves the right space before the file loads and the layout doesn't shift
  3. Because srcset requires it
  4. To 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).