Skip to content
AgileLogics
7 min read

Responsive Images

Prerequisites: Images

A phone doesn't need a 2400-pixel-wide photo, and a large high-density screen looks blurry with a small one. Responsive images let the browser choose.

Different sizes: srcset with w and sizes

<img
  src="img/market-1200.jpg"
  srcset="img/market-480.jpg 480w,
          img/market-800.jpg 800w,
          img/market-1200.jpg 1200w,
          img/market-2000.jpg 2000w"
  sizes="(min-width: 1000px) 50vw, 100vw"
  alt="Fruit stalls at the Saturday market"
  width="1200" height="800">
  • srcset lists the files and their real widths (480w = 480 pixels wide).
  • sizes tells the browser how wide the image will be displayed: half the viewport on screens at least 1000 pixels wide, otherwise the full viewport. The first matching condition wins. The browser needs this before CSS has loaded.
  • The browser multiplies the display width by the screen's pixel density and picks a suitable file. On a 400-pixel-wide phone with 2× density, the image is 400 CSS pixels = 800 device pixels wide, so it will likely pick market-800.jpg.
  • src is the fallback for very old browsers.

The browser is free to choose differently — for example a smaller file on a slow connection — so treat srcset as a list of options, not commands.

Fixed-size images: x descriptors

For an image always shown at the same size (a logo, an avatar):

<img src="avatar-64.png" srcset="avatar-64.png 1x, avatar-128.png 2x" alt="Sam Lee" width="64" height="64">

Different images: picture

picture lets you change the image itself — art direction — or offer newer formats:

<picture>
  <source media="(max-width: 600px)" srcset="img/team-portrait.jpg" width="600" height="800">
  <source type="image/avif" srcset="img/team-wide.avif">
  <source type="image/webp" srcset="img/team-wide.webp">
  <img src="img/team-wide.jpg" alt="Our team of six bakers in front of the shop" width="1600" height="900">
</picture>
  • The browser uses the first source that matches (media) and that it supports (type), otherwise the img.
  • The img is required: it's what actually displays, and it carries the alt, width/height, loading, and CSS classes.
  • A source can have its own width/height when its aspect ratio differs.

Lazy loading and priority

<img src="img/hero.jpg" alt="Loaves cooling on racks" width="1600" height="900" fetchpriority="high">
<img src="img/footer-map.png" alt="Map showing the shop two blocks from the station" width="800" height="400" loading="lazy">
  • loading="lazy" delays images until they're about to scroll into view. Use it for images below the fold — never on the main image at the top, or it loads later.
  • fetchpriority="high" hints that an image is important (usually the largest image in the first screen). Supported in Chrome and Edge 101, Safari 17.2, and Firefox 132.
  • decoding="async" lets the browser decode the image off the main thread.

The validator checks sizes

<!-- Wrong: w descriptors without sizes -->
<img src="a-800.jpg" srcset="a-400.jpg 400w, a-800.jpg 800w" alt="Example" width="800" height="600">
error: When the 'srcset' attribute has any image candidate string with a width descriptor, the 'sizes' attribute must also be specified.

Browsers are adding sizes="auto" for lazy-loaded images (the browser measures the layout itself; Chrome and Edge since version 126). Because older browsers ignore it, list real sizes after it as a fallback: sizes="auto, (min-width: 1000px) 50vw, 100vw".

Practice

A blog shows article images full width on phones and at 720 pixels on screens 800 pixels and wider. Write the img using files post-400.jpg, post-720.jpg, and post-1440.jpg (original size 1440×960).

Show solution
<img src="post-720.jpg"
     srcset="post-400.jpg 400w, post-720.jpg 720w, post-1440.jpg 1440w"
     sizes="(min-width: 800px) 720px, 100vw"
     alt="Describe the image here" width="1440" height="960" loading="lazy">

Leave off loading="lazy" if the image is at the top of the page.

Related developer tools