Command Palette

Search for a command to run...

Hectal
PHASE 5Intermediate ~28 min· topic 1 of 7

Topic 5.1

Responsive Images

In one line

With srcset, sizes and <picture> you list several versions of an image and let the browser download only the one that fits the screen, instead of sending a huge photo to every phone.

Think of it like this

Buying shoes. You don't buy the biggest pair in the shop and hope they fit; you tell the shopkeeper your size and get the pair that fits. With responsive images, the page tells the browser which sizes exist, the browser knows its own screen, and it fetches the pair that fits.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Responsive image
An image that downloads and shows a version suited to the screen it's on.
srcset
An img (or source) attribute listing several image files the browser may choose from.
w descriptor
A number with w after a file in srcset, like 800w: the file's real width in pixels.
x descriptor
A number with x after a file in srcset, like 2x: the screen density the file is made for.
sizes
An attribute that tells the browser how wide the image will be shown on the page, so it can choose a file before CSS loads.
Viewport
The visible part of the browser window. 100vw means its full width.
Device pixel ratio (DPR)
How many physical screen dots make up one CSS pixel. Sharp phone screens are often 2 or 3.
picture
An element holding several source options and one img. The first matching source is used.
Art direction
Showing a different crop or picture on different screen sizes, not only a different size of the same picture.

Step by step

01Start with one image

A plain <img> always downloads the same file, whatever the screen. This 1600px photo costs a phone just as much as a desktop.

index.htmlwhole filehtml
<img src="cat-1600.jpg" alt="A ginger cat asleep on a windowsill" width="1600" height="900">

02List the versions with srcset

Export the same picture at a few widths (an image tool or a build step does this) and list them with their real widths. Keep src as the fallback.

On its own, srcset with w makes the browser assume the image is as wide as the viewport (sizes defaults to 100vw).

index.htmlwhole filehtml
<img src="cat-800.jpg"
     srcset="cat-400.jpg 400w, cat-800.jpg 800w, cat-1600.jpg 1600w"
     alt="A ginger cat asleep on a windowsill" width="1600" height="900">

03Say how wide it will be with sizes

In this layout the image fills the screen on phones and takes a third of the width in a three-column grid on wide screens. sizes describes exactly that, so the browser can choose before it has downloaded the CSS.

Values in sizes can be vw, px, em or calc(), but not percentages: the browser hasn't done layout yet, so it doesn't know what 50% of the parent is.

index.htmlwhole filehtml
<img src="cat-800.jpg"
     srcset="cat-400.jpg 400w, cat-800.jpg 800w, cat-1600.jpg 1600w"
     sizes="(max-width: 700px) 100vw, 33vw"
     alt="A ginger cat asleep on a windowsill" width="1600" height="900">

04Watch the browser do the maths

On a phone 390px wide with DPR 3: the slot is 100vw = 390px, times 3 = 1170 real pixels needed. The smallest file at least that wide is cat-1600.jpg.

On a laptop 1440px wide with DPR 1: the slot is 33vw ≈ 475px, times 1 = 475. The browser picks cat-800.jpg, the smallest file that covers it. The big file is never downloaded.

Watch the browser do the mathsdiagram
Rendering diagram…

05Fixed-size images use x descriptors

A logo is always 160px wide, so the layout never changes its size. Only the screen density matters, which is what x describes. No sizes needed.

index.htmlwhole filehtml
<img src="logo-160.png" srcset="logo-160.png 1x, logo-320.png 2x, logo-480.png 3x"
     alt="Hectal" width="160" height="40">

06Change the picture itself with <picture>

Art direction: on wide screens show the whole scene; on narrow screens show a close crop, so the cat isn't a tiny dot. Each <source> can have its own srcset and sizes. The <img> comes last and is required.

type lets you offer modern formats: a browser that can't decode AVIF skips that source and moves to the next one.

index.htmlwhole filehtml
<picture>
  <source media="(min-width: 800px)" srcset="scene-wide.avif" type="image/avif">
  <source media="(min-width: 800px)" srcset="scene-wide.jpg">
  <source srcset="cat-closeup.avif" type="image/avif">
  <img src="cat-closeup.jpg" alt="A ginger cat asleep on a windowsill" width="800" height="800">
</picture>
Change the picture itself with <picture>diagram
Rendering diagram…

Try it yourself

  1. 1

    Predict the pick

    Before running the first example, look at the info line under the preview: your viewport width and DPR. Work out slot width × DPR yourself, then press Run. Did the browser pick the file you predicted?

  2. 2

    Change sizes

    In the first example, change sizes to 100px. Predict: the slot is now tiny, so the browser should pick the 400w file. Run it and check. (If it still shows a bigger file, the browser reused one from its cache, which the spec allows. Reload the page and try again.)

  3. 3

    Remove the media condition

    In the picture example, delete media="(min-width: 500px)" from the second source. Now it matches everywhere: which picture do you predict on a narrow preview?

Code & diagrams

srcset and sizes: which file did the browser pick? New tab

The pictures are small SVG images written into the code (data URIs) so this works offline. Which one you get depends on your screen, so your console may differ from someone else's.

Preview

Press Run to see the page.

Console

Nothing logged yet.
picture: the first matching source wins New tab

The first source is skipped because no browser knows the type `image/x-future`. Then a narrow preview shows the close-up and a wide one shows the wide crop. Drag the preview wider or narrower and press Run.

Preview

Press Run to see the page.

Console

Nothing logged yet.
Reading the choices from JavaScript New tab

With x descriptors the shown size comes from `width` (or the 1x file), whatever file is downloaded. A 2x file is drawn at half its pixel size, so it looks sharp, not big.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Candidates in srcset: 2
sizes attribute: ""
Has x descriptors: true
Shown width in CSS pixels: 160

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Use w descriptors without sizes

Remove the sizes attribute from an image that is shown at 300px wide in a sidebar, on a 1440px laptop.

terminal
$ DevTools → Network
── what you'll see ──
The 1600w file is downloaded for a 300px-wide image.

Break #2

Forget the img inside picture

Write a <picture> with two <source> elements and no <img>.

terminal
$ the page shows
── what you'll see ──
Nothing at all: no image, no alt text, no error in the console.

Myth vs fact

Myth

The browser picks the image from my CSS width.

Fact

It picks from sizes, often before your CSS has even arrived. If sizes and your CSS disagree, the browser downloads the wrong file and CSS then scales it.

Myth

srcset makes the image look different on phones.

Fact

srcset is for different sizes of the same picture. To show a different crop or a different picture, use <picture> with media (art direction).

Myth

Bigger files always look better on retina screens.

Fact

Past about 2x the eye barely sees the difference. Many sites cap at 2x to save bytes, even on DPR-3 phones.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Image selection happens in the preload scanner, a fast pass over the HTML that starts downloads before the main parser and before CSS. That's why sizes exists at all: the browser has no layout information yet.

  • ▸

    Chromium supports sizes="auto" for loading="lazy" images: since a lazy image is fetched after layout, the browser can use its real rendered width. Other browsers are still adding it; keep an explicit fallback such as sizes="auto, (max-width: 700px) 100vw, 33vw".

  • ▸

    Inside <picture>, <source> can carry its own width and height attributes, so art-directed crops with different aspect ratios still reserve the right space (see Topic 5.2).

  • ▸

    Image CDNs and frameworks (for example next/image) generate the srcset widths for you. You still need to give a correct sizes; it is the part tools can't guess.

Remember this

  1. 1

    A responsive image is an image that adapts to the screen it is shown on. The problem it solves: one 2400-pixel-wide photo looks sharp on a big monitor, but a phone that shows it 400 pixels wide still downloads every byte. That wastes data, battery and time. The fix is to make several versions of the same picture (for example 400, 800 and 1600 pixels wide) and let the browser choose.

  2. 2

    srcset with w descriptors lists the versions and their real widths in pixels: srcset="cat-400.jpg 400w, cat-800.jpg 800w, cat-1600.jpg 1600w". The w means "this file is this many pixels wide". You describe the files; the browser does the maths. The plain src stays as the fallback for very old browsers and as the default.

  3. 3

    sizes tells the browser how wide the image will be on the page, before any CSS has loaded: sizes="(max-width: 600px) 100vw, 50vw" means "on screens up to 600px wide the image fills the whole viewport width (100vw); otherwise it takes half (50vw)". A viewport is the visible area of the browser window, and 1vw is 1% of its width. The browser reads the conditions left to right and uses the first one that matches; the last value has no condition and is the default. Without sizes, the browser assumes 100vw.

  4. 4

    The browser then multiplies: slot width × device pixel ratio (DPR, how many physical screen dots make one CSS pixel: 1 on many laptops, 2 or 3 on phones). A 400px slot on a DPR-2 phone needs about 800 real pixels, so it picks cat-800.jpg. The spec lets the browser decide the final choice: on a slow connection it may pick a smaller file, and if a larger version is already in its cache it may reuse that.

  5. 5

    x descriptors are the simpler form for images shown at a fixed size, like a 200px logo: srcset="logo.png 1x, logo@2x.png 2x". The browser picks by DPR alone. Use x for fixed-size images and w + sizes for images whose width changes with the layout. You can't mix x and w in one srcset.

  6. 6

    <picture> wraps several <source> elements and one <img>. The browser checks each <source> from the top and uses the first one that matches its media condition and/or its type (file format). That gives you art direction: showing a different crop on phones (a close-up of a face) and on desktops (the wide scene). It also lets you offer a modern format such as AVIF with a JPEG fallback. The <img> inside is required: it is what is actually shown, and its alt, width, height and CSS apply whichever file wins.

Explain it without notes

01

What problem do srcset and sizes solve, and how does the browser use them to choose a file?

02

When would you use x descriptors and when w descriptors?

03

What is the difference between srcset resolution switching and <picture> art direction?

04

Why can't sizes use percentages?

Practice

01

Write an <img> for a hero banner that fills the full width on every screen, with files hero-640.jpg, hero-1280.jpg and hero-1920.jpg.

02

Write a <picture> that offers photo.avif and photo.webp before a photo.jpg fallback.

03

A product image is 100% wide below 600px, 50% wide up to 1000px, and a fixed 400px above that. Write its sizes.

Trade-offs

  • ↔

    More versions in srcset means better-fitting downloads but more files to generate, store and cache; three to five widths is a common balance.

  • ↔

    Art direction gives the best picture per screen but means designing and maintaining several crops; use it only where a single image really doesn't work small.

Done when you can

  • I can write srcset with w descriptors and a matching sizes.

  • I can work out which file a browser will pick from slot width and DPR.

  • I know when to use x descriptors instead.

  • I can use <picture> for art direction and modern formats with a fallback <img>.

  • I can check which file was downloaded with currentSrc or the Network panel.