Responsive Images: srcset, sizes and picture

Stop sending a 2000 pixel photograph to a phone. Learn width descriptors, the sizes attribute the browser cannot work out for itself, and when picture beats srcset.

Concept

One image file cannot serve every screen well. A photograph sized for a desktop wastes bandwidth on a phone; one sized for a phone looks soft on a laptop. Responsive images let you offer several files and let the browser choose.

There are two distinct problems, and they need different tools:

  • Resolution switching - the same picture at several sizes. Use srcset and sizes on a plain img.
  • Art direction - a genuinely different crop or a different image for narrow screens. Use picture with source and media.
Three panels: srcset with width descriptors and a sizes attribute choosing a file for a phone and a desktop; a picture element with a media query swapping a wide crop for a square crop; and a picture element offering AVIF and WebP before a JPEG fallback.
Resolution switching, art direction and format negotiation, side by side.

srcset with width descriptors

<img src="hall-800.jpg"
     srcset="hall-400.jpg   400w,
             hall-800.jpg   800w,
             hall-1600.jpg 1600w"
     sizes="(max-width: 700px) 100vw, 50vw"
     alt="An empty lecture hall"
     width="1600" height="900">

Read it as a table of offers. The w value is the real pixel width of each file - not a screen size, and not a breakpoint. The browser combines that with sizes and the device pixel ratio to decide.

Why sizes is required

This is the part that trips everyone up. The browser starts downloading the image before it has applied any CSS, so it does not yet know how wide the image will be laid out. sizes is you telling it in advance.

sizes="(max-width: 700px) 100vw, 50vw"

  Viewport 700px or narrower  ->  the image fills the viewport width
  Wider                       ->  the image is half the viewport width

The list is read left to right and the first matching condition wins, so the last entry has no condition and acts as the default. Omit sizes and the browser assumes 100vw, which usually means it downloads a file two or three times bigger than needed.

Pixel density descriptors

For a fixed size image such as a logo or an avatar, the simpler x form is enough:

<img src="avatar-1x.png"
     srcset="avatar-1x.png 1x, avatar-2x.png 2x, avatar-3x.png 3x"
     alt="" width="48" height="48">

No sizes is needed here because the layout width never changes. Do not mix w and x descriptors in one srcset.

picture for art direction

When a wide landscape crop turns into an unreadable strip on a phone, you do not want a smaller version of the same crop. You want a different picture.

<picture>
  <source media="(min-width: 1000px)" srcset="stall-wide.jpg">
  <source media="(min-width: 600px)"  srcset="stall-medium.jpg">
  <img src="stall-square.jpg" alt="Two cooks working at a roadside stall" width="800" height="800">
</picture>

The browser takes the first source whose media condition matches and stops looking, so order from largest to smallest. The img at the end is not optional: it is where alt, width, height and loading live, and it is the fallback when no source matches.

picture for modern formats

<picture>
  <source type="image/avif" srcset="lamp.avif">
  <source type="image/webp" srcset="lamp.webp">
  <img src="lamp.jpg" alt="A brass lamp on a workbench" width="1200" height="800">
</picture>

The browser picks the first type it can decode. AVIF and WebP are typically twenty five to fifty percent smaller than an equivalent JPEG, and the JPEG stays there for anything that cannot handle them.

Combining everything

<picture>
  <source type="image/avif"
          srcset="hall-400.avif 400w, hall-800.avif 800w, hall-1600.avif 1600w"
          sizes="(max-width: 700px) 100vw, 50vw">
  <source type="image/webp"
          srcset="hall-400.webp 400w, hall-800.webp 800w, hall-1600.webp 1600w"
          sizes="(max-width: 700px) 100vw, 50vw">
  <img src="hall-800.jpg"
       srcset="hall-400.jpg 400w, hall-800.jpg 800w, hall-1600.jpg 1600w"
       sizes="(max-width: 700px) 100vw, 50vw"
       alt="An empty lecture hall" width="1600" height="900" loading="lazy">
</picture>

Three formats, three sizes each, one alt text. Generate these variants with a build tool or an image service; writing them by hand does not scale.

Important rules

  • src is still required on the img as the fallback for browsers that ignore srcset.
  • The w descriptor must be the true pixel width of the file. A wrong number produces a wrong choice.
  • Every source must be before the img, and there must be exactly one img.
  • alt, width, height, loading and class all go on the img, never on source.
  • The browser decides, not you. It may keep a larger cached file rather than downloading a smaller one, and that is correct behaviour.
  • Do not mix w and x descriptors in the same srcset.

Common mistakes

  • Omitting sizes, so the browser assumes full viewport width and over downloads.
  • Writing breakpoints into the w descriptors instead of actual file widths.
  • Ordering picture sources smallest first, so the first one always matches.
  • Putting alt on source, where it does nothing.
  • Using picture for simple resolution switching, where srcset alone is enough.
  • Generating five sizes of an image that is only ever displayed at two hundred pixels.

Best practices

  • Use srcset and sizes for photographs in flexible layouts; use picture only for a real change of crop or format.
  • Three or four widths is usually enough. Doubling each time is a good rule.
  • Keep sizes in step with the stylesheet. When the layout changes, sizes has to change too.
  • Automate variant generation.
  • Check the actual downloaded file in the Network panel at several window widths. It is the only way to know it works.

Practice

  1. Build an image with three widths and a sizes attribute for a layout that is full width on phones and half width above eight hundred pixels.
  2. Open the Network panel, resize the window through the breakpoint, and confirm which file was fetched each time.
  3. Use picture to swap a wide crop for a square crop below six hundred pixels.
  4. Add AVIF and WebP sources before a JPEG and compare the transferred bytes.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All HTML notes →
HTML

Audio and Video in HTML

Native media playback with no plugin. Learn the controls, the source fallback pattern, autoplay rules that will catch you out, and why captions are no...

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.