One image, many screens
A photo that fills a desktop monitor might be 1600 pixels wide. On a phone the same photo is shown about 400 pixels wide. If every visitor downloads the big version, phone users wait longer and use more mobile data for detail they cannot see.
HTML lets you offer several versions of one image and leave the choice to the browser, which knows the screen size, the pixel density and sometimes even the network speed.
srcset with width descriptors
First create a few copies of the image at different widths, for example 480, 800 and 1600 pixels. Then list them in srcset, each followed by its real width and a w:
<img
src="market-800.jpg"
srcset="market-480.jpg 480w, market-800.jpg 800w, market-1600.jpg 1600w"
sizes="(max-width: 700px) 100vw, 700px"
alt="Vegetable stalls at a busy morning market"
width="1600" height="1067">srcis the fallback for very old browsers, and the default choice.srcsetlists the candidates.480wmeans "this file is 480 pixels wide", not "show it at 480 pixels".sizestells the browser how wide the image will be displayed. Here: on screens up to 700 pixels wide it fills the viewport width (100vw); otherwise it is shown at 700 pixels.
Why does the browser need sizes? It starts downloading images very early, before the CSS has worked out the page layout. Without sizes it assumes the image is as wide as the whole screen and may download a file that is far too large.
The browser then does the maths. On a phone 400 pixels wide with a 2× screen, the image is shown at 400 CSS pixels, which needs about 800 real pixels, so it picks market-800.jpg. On a desktop it needs 700 CSS pixels; on a 2× screen that is 1400 pixels, so it picks the 1600 version. You never have to write that logic yourself.
srcset with density descriptors
For images that are always shown at the same size, such as a logo or an avatar, density descriptors are simpler:
<img src="avatar-96.png" srcset="avatar-96.png 1x, avatar-192.png 2x" alt="Asha Rao" width="96" height="96">A normal screen gets the 96-pixel file; a high-density "retina" screen gets the sharper 192-pixel one. No sizes is needed, because the display size never changes.
picture: modern formats with a fallback
AVIF and WebP files are usually much smaller than JPEG at the same quality. Every current major browser supports WebP, and AVIF support is very wide too, but the picture element lets you offer them safely to everyone:
<picture>
<source type="image/avif" srcset="market-800.avif">
<source type="image/webp" srcset="market-800.webp">
<img src="market-800.jpg" alt="Vegetable stalls at a busy morning market" width="800" height="533">
</picture>The browser reads the source elements in order and uses the first one whose type it supports. If none match, it uses the img. The img is not optional: it is the element that actually displays the picture, it carries the alt text and the size, and source elements only change which file it loads. You can combine this with srcset and sizes on each source for several widths in several formats.
picture for art direction
Sometimes a phone needs a different crop, not just a smaller file: a wide landscape banner may be unreadable on a narrow screen. source can take a media condition:
<picture>
<source media="(max-width: 600px)" srcset="team-portrait.jpg">
<img src="team-wide.jpg" alt="Our team of five at the office" width="1600" height="600">
</picture>Narrow screens get the portrait crop; everything else gets the wide one. With media, the browser must follow your condition. With srcset alone it is free to choose, which is what you want for plain resizing.
Making the files
You do not need special software. Export a few widths from any photo editor, or use the free Image Compressor on this site to resize and convert to WebP in your browser. Many hosting platforms and frameworks can generate the sizes automatically as well.
A practical starting set for content images is three widths, roughly 480, 800 and 1200 to 1600 pixels. More versions give diminishing returns.
Common mistakes
- Treating
800was a display size. It is the file's width; display size comes fromsizesand CSS. - Leaving out
sizeswith width descriptors, so the browser assumes full-screen width and downloads too much. - Putting
alton asource.sourcehas noalt; it belongs on theimg. - Forgetting
widthandheight, which causes layout shift no matter how clever thesrcsetis. - Using
picturefor simple resizing. Animgwithsrcsetandsizesis enough; keeppicturefor formats and art direction.
Exercise
You have hero-600.webp, hero-1200.webp, hero-600.jpg and hero-1200.jpg. The hero image is shown at full screen width on phones and at 900 pixels on wider screens (from 900px up). Write a picture element that offers WebP with a JPEG fallback, both in two widths, with correct sizes, alt text, width and height (the large files are 1200 × 675).
Show one possible answer
<picture>
<source type="image/webp"
srcset="hero-600.webp 600w, hero-1200.webp 1200w"
sizes="(min-width: 900px) 900px, 100vw">
<img src="hero-1200.jpg"
srcset="hero-600.jpg 600w, hero-1200.jpg 1200w"
sizes="(min-width: 900px) 900px, 100vw"
alt="Students working together at a long wooden table"
width="1200" height="675">
</picture>Key points
- srcset lists several versions of the same image; the browser picks the best one for the screen.
- Width descriptors (480w) plus a sizes attribute tell the browser how wide the image will be displayed.
- Density descriptors (1x, 2x) are simpler and suit images that always display at one fixed size.
- picture with source elements offers modern formats (AVIF, WebP) with a JPEG or PNG fallback in the img.
- The img inside picture is still required and still needs alt, width and height.