Skip to main content
Back to module
Accessible & Responsive Interfaces · Responsive Design

Responsive images

Reading1h 10mLesson 3 of 3 in this module

Serve the right image size and format with srcset, sizes, picture and lazy loading.

Why this matters

Images are usually the heaviest part of a page. Sending a 2400 px wide photo to a 360 px phone wastes data and slows the page, which hits people on prepaid plans and slow connections hardest. Images without dimensions also make the page jump as they load (layout shift), which is disorienting for everyone and especially for people with cognitive or vision impairments who lose their place. HTML has built-in tools for all of this.

Step 1: let images shrink

img { max-width: 100%; height: auto; }

max-width: 100% stops an image overflowing its container; height: auto keeps the aspect ratio. Put this in every stylesheet.

Step 2: always set width and height

<img src="trail-800.jpg" width="1600" height="900" alt="Hikers on a ridge at sunrise">

The numbers are the image's intrinsic size (or any pair with the same ratio). Browsers use them to reserve the right space before the file arrives, and the CSS above still makes it responsive. This is the single biggest fix for Cumulative Layout Shift. CSS aspect-ratio does the same job for elements without intrinsic dimensions.

Step 3: srcset and sizes (resolution switching)

When the same picture is needed at different sizes, offer several files and let the browser choose:

<img
  src="trail-800.jpg"
  srcset="trail-480.jpg 480w, trail-800.jpg 800w, trail-1600.jpg 1600w"
  sizes="(min-width: 60rem) 50vw, 100vw"
  width="1600" height="900"
  alt="Hikers on a ridge at sunrise">
  • srcset lists files with their real widths (480w means 480 pixels wide).
  • sizes tells the browser how wide the image will be displayed: half the viewport on wide screens, the full viewport otherwise. The browser needs this before CSS loads.
  • The browser multiplies the display width by the screen's pixel density and picks the smallest file that is big enough. A phone at 360 px and 3x density needs about 1080 px, so it takes trail-1600.jpg; a 1x laptop showing it at 600 px takes trail-800.jpg.
  • src is the fallback for very old browsers.

For a fixed-size image (like an avatar) use density descriptors instead: srcset="avatar.jpg 1x, avatar@2x.jpg 2x".

Step 4: picture for formats and art direction

<picture> lets you offer modern formats with a fallback, or a different crop at different sizes:

<picture>
  <source type="image/avif" srcset="team.avif">
  <source type="image/webp" srcset="team.webp">
  <img src="team.jpg" width="1200" height="800" alt="The support team at their desks">
</picture>

The browser uses the first <source> it supports. The <img> inside is required: it is what actually renders, and it carries alt, width, height and loading. For art direction, add media to a source, for example a tight square crop for phones:

<picture>
  <source media="(min-width: 40rem)" srcset="banner-wide.jpg">
  <img src="banner-square.jpg" width="800" height="800" alt="A bowl of ramen">
</picture>

Step 5: lazy-load what is off screen

<img src="gallery-3.jpg" loading="lazy" width="800" height="600" alt="...">

loading="lazy" delays images below the fold until the user scrolls near them. Do not lazy-load the main image at the top of the page (often the Largest Contentful Paint element); it should load immediately, and fetchpriority="high" can help it start sooner.

Alt text still matters

Every responsive technique above is about the file. The alt attribute is still what screen reader users get, and it goes on the <img>, not on <source>. Describe what the image conveys in context; use alt="" for decorative images. A chart or diagram needs a text equivalent nearby (a sentence or a data table) when alt is not enough.

Worked example: a recipe page

The page has a hero photo, a step-by-step gallery of six photos further down and a decorative divider. A good result:

  • Hero: srcset with 3 widths, sizes="100vw", width/height, no loading="lazy", a descriptive alt.
  • Gallery: loading="lazy", width/height, alt text for each step ("Folding the dough in half").
  • A <picture> for the hero offering AVIF and WebP before the JPEG.
  • Divider: alt="" or, better, a CSS border.

Common mistakes

  • srcset with w descriptors but no sizes: the browser assumes 100vw and may download a file far too big.
  • Missing width and height, causing layout shift.
  • Lazy-loading the hero image.
  • Putting alt on <picture> or <source> (it belongs on <img>).
  • Using CSS background images for content images, which have no alt text.

Quick recap

  • img { max-width: 100%; height: auto; } plus width/height attributes.
  • srcset + sizes for resolution switching; <picture> for formats and art direction.
  • loading="lazy" below the fold only.
  • alt on the <img>, always.
  • Reference: MDN: Responsive images and W3C WAI Images tutorial.

Try it

Open the Do tab and make a gallery page's images responsive. (The preview has no network, so images show as broken; the checks look at your markup and CSS.)

Also in this lesson: