picture and art direction
When the image needs different cropping or different formats per breakpoint — the picture element does what srcset alone cannot.
srcset solves "the same image at different file sizes". <picture> solves the harder cases: the same image cropped differently per breakpoint, or served in a different format per browser. Both are common; both need a different element.
<picture> is a wrapper around <img> that lets you list alternative sources. The browser walks the sources and picks the first one whose conditions match. If none match, it falls back to the <img> inside.
Art direction with media
"Art direction" is the term for serving a different image based on the screen — not just a different size of the same image, a genuinely different crop or composition.
A classic example: on desktop, your hero is a wide landscape shot of a baker pulling bread from an oven. On mobile, that same shot crammed into a portrait phone screen shows mostly oven door. You want a tighter, vertical crop for mobile.
<picture>
<source media="(min-width: 1024px)" srcset="hero-wide.jpg">
<source media="(min-width: 480px)" srcset="hero-square.jpg">
<img src="hero-portrait.jpg" alt="A baker pulling sourdough from the oven"
width="1200" height="800">
</picture>Reading top to bottom: on viewports at least 1024px wide, the browser uses hero-wide.jpg. On viewports at least 480px wide (and below 1024px), hero-square.jpg. Below 480px, the fallback <img> uses hero-portrait.jpg.
Each <source> has its own srcset (so you can combine art direction with resolution switching). The order matters — the first matching source wins, so list more specific media queries first.
The <img> is mandatory inside <picture>. It carries the alt and the width/height. It is also what older browsers without <picture> support fall back to.
A useful picture: <picture> is a parent; the <source>s are options on a menu; the <img> is the default order. The browser reads the menu and asks "which option matches my screen?" — then takes that one.
Always put the most specific (largest) media query first. The browser stops at the first match — if you list (min-width: 480px) before (min-width: 1024px), every screen 480px+ takes the smaller crop and the desktop crop is never used.
<picture><source srcset="wide.jpg" media="(min-width: 768px)"><source srcset="square.jpg" media="(min-width: 1200px)"><img src="default.jpg"></picture>. A user on a 1400px-wide screen loads the page. Which image do they get?Format fallback chains
Different image formats give very different file sizes for the same image quality. AVIF is usually 30-50% smaller than WebP, which is 25-30% smaller than JPEG — but not every browser decodes every format.
<picture> lets you list multiple sources by type, and the browser picks the first format it understands.
<picture>
<source srcset="loaf.avif" type="image/avif">
<source srcset="loaf.webp" type="image/webp">
<img src="loaf.jpg" alt="A round sourdough loaf"
width="800" height="600">
</picture>Reading: try AVIF first. If the browser does not understand AVIF, try WebP. If it does not understand WebP either, fall back to the <img> JPEG.
The order is "best to worst" — the format that gives the smallest file goes first; older formats go later as fallbacks. Browser support for AVIF is now in every recent evergreen browser, but JPEG is still the universal floor.
The type attribute is the MIME type of the file. The browser uses the type — not the file extension or the file's bytes — to decide if it can decode the image. Get the type wrong (image/jpg instead of image/jpeg) and the source is silently skipped.
You can combine art direction and format fallback in one <picture>:
<picture> <!-- Desktop, AVIF then WebP then JPEG --> <source media="(min-width: 1024px)" srcset="hero-wide.avif" type="image/avif"> <source media="(min-width: 1024px)" srcset="hero-wide.webp" type="image/webp"> <source media="(min-width: 1024px)" srcset="hero-wide.jpg" type="image/jpeg"> <!-- Mobile/tablet, AVIF then WebP then JPEG --> <source srcset="hero-portrait.avif" type="image/avif"> <source srcset="hero-portrait.webp" type="image/webp"> <img src="hero-portrait.jpg" alt="..." width="1200" height="800"> </picture>
Six sources plus the fallback. The browser picks the most specific media-matching, format-supported pair. Verbose — but each line is doing a job.
Image build tools (Next.js, Astro, Cloudinary, ImgIX) generate the right combinations of formats and sizes for you. For a manually-curated site, the markup gets long fast — that's the price of squeezing every byte. Most teams reach for an image CDN before hand-writing chains like the one above.
picture vs srcset alone
When do you need <picture> and when does <img srcset> suffice?
Use <img srcset sizes> alone when:
- The image is the same picture at different resolutions (logo at 1x/2x, a hero at multiple widths).
- The browser should pick based on viewport size and pixel density.
Reach for <picture> when:
- You need a different crop on mobile vs desktop (art direction).
- You want to serve modern formats (AVIF, WebP) with older formats as fallbacks.
- You want to combine multiple
<source>rules with their ownsrcsetper source.
A useful rule: srcset picks between sizes; <picture> picks between sources. If your alternatives are all the same image at different sizes, srcset alone is enough. If your alternatives differ by crop, format, or some other axis, <picture> is the tool.
srcset works inside <source> — you can have a <source> with both media (for art direction) and srcset with w descriptors (for resolution switching). That is the most general pattern.
<picture> <source media="(min-width: 1024px)" srcset="hero-wide-800.jpg 800w, hero-wide-1600.jpg 1600w" sizes="(min-width: 1280px) 1200px, 100vw"> <img src="hero-portrait-800.jpg" srcset="hero-portrait-400.jpg 400w, hero-portrait-800.jpg 800w" sizes="100vw" alt="..." width="800" height="1000"> </picture>
Desktop sources have their own resolution-switched srcset; the mobile fallback <img> also has its own srcset. The browser picks the right source by media query, then picks the right file from that source by srcset/sizes.
<source srcset="loaf.avif" type="image/avif"> to a picture. A user's browser supports AVIF. What happens?