skip to content

What does the type attribute on a <source> inside an HTML <picture> do, and why does the order of the source elements matter when offering AVIF, WebP and JPEG?

level: middleimportance: should knowfreq 56%

answer

  1. a decode test before any request
  2. document order is the ranking
  3. earlier wins, unlike the cascade
  4. the unconditional candidate goes last
  5. a mislabelled file breaks, it does not retry

basics

~20 s

type declares a candidate's MIME type, so a browser that cannot decode that format skips the source without downloading it. Selection stops at the first supported source, so sources must be listed best-format first, with the universally supported file on the trailing img.

solid answer

~50 s

`type` states the candidate's MIME type — `image/avif`, `image/webp`, `image/jpeg` — and the browser uses it as a pre-flight test: if it cannot decode that format it skips the `<source>` entirely, without fetching anything. Selection is a first-match walk in document order, so the list is a priority order, not a set of alternatives: put AVIF first, WebP next, and leave the universally-decodable JPEG or PNG on the trailing `<img>` as the floor. Reverse the order and every browser takes the WebP, because the walk stops at the first candidate it can handle and never looks further. The important consequence is that this is a *client-side* negotiation expressed entirely in markup — you ship one document and each browser resolves it, which is why you never need to sniff the user agent to decide which format to serve.

go deeper

for a junior

Recall the shape: modern format first, plain JPEG or PNG on the img at the end, and a type attribute on each modern source so unsupported browsers skip it.

for a middle

Explain that type is a decode check made before any network request and that selection is first-match in document order, then state plainly that this is the opposite of the CSS cascade's later-wins rule.

for a senior

Be ready for the failure modes: a 404 on the selected variant is a broken image with no fallback, a mislabelled type breaks decoding, and combining media with type needs an unconditional entry per crop group.

for a principal

Decide where format negotiation lives — markup versus an image CDN — and defend it on caching, build-pipeline and debuggability grounds, not on which one is fashionable.

## What the attribute actually asserts `type` on a `<source>` inside `<picture>` carries a MIME type: `image/avif`, `image/webp`, `image/jpeg`, `image/png`, `image/svg+xml`. It is a claim about the file the `srcset` points at, and the browser treats it as a gate. Before considering the candidate at all, it asks itself "can I decode this MIME type?" If the answer is no, the whole `<source>` is skipped — no request, no wasted bytes, no broken image. This is what makes format fallback possible in pure markup. Without `type` the browser would have to download the file to discover it cannot decode it, which defeats the entire point. ```html <picture> <source type="image/avif" srcset="hero.avif"> <source type="image/webp" srcset="hero.webp"> <img src="hero.jpg" alt="Sunrise over the bay" width="1200" height="675"> </picture> ``` ## Order is priority, not preference The selection algorithm walks `<source>` children in document order and takes the **first** one that passes every test it declares — `type` decodable, and `media` matching if present. It then resolves that source's `srcset` and stops. Later sources are never consulted. So the list is strictly a ranking. Newest and most efficient format first; each subsequent entry is a wider-support, larger-file compromise; the `<img>` at the end carries the format every browser can render. Get the order wrong — WebP above AVIF — and the AVIF is dead markup, because any browser that could have taken it also handles WebP and stops there. This is a real difference from CSS, where a later rule of equal specificity wins. In `<picture>`, earlier wins. Candidates who carry the cascade intuition over to markup get this backwards under pressure. ## Format fallback and art direction are the same mechanism `media` and `type` are two conditions on the same walk, and a `<source>` may carry both. That composes, but combinatorially: three crops times three formats is nine `<source>` elements, and each must appear in an order that is correct on *both* axes. ```html <picture> <source media="(min-width: 800px)" type="image/avif" srcset="wide.avif"> <source media="(min-width: 800px)" type="image/webp" srcset="wide.webp"> <source media="(min-width: 800px)" srcset="wide.jpg"> <source type="image/avif" srcset="square.avif"> <source type="image/webp" srcset="square.webp"> <img src="square.jpg" alt="Ada at the analytical engine"> </picture> ``` Note the shape: the crop is the outer grouping and the format is the inner one, with a no-`type` JPEG closing each crop group so a browser that matches the wide crop but decodes neither modern format still gets a wide image rather than falling through to the square fallback on the `<img>`. Miss that line and desktop users on an older browser get the phone crop. This is the single most common structural bug in hand-written combined `<picture>` markup, and it is the reason such markup is usually generated by a component rather than typed. ## What type does not do `type` does not transform anything, and it does not verify anything. If you label a JPEG as `image/avif`, a browser that supports AVIF will happily select it and then fail to decode the actual bytes — you get a broken image, not a graceful retry. There is no re-selection after a load failure: once a source is chosen, that choice is final, and a 404 or a corrupt file produces a broken image rather than a fall-through to the `<img>`. The `<img>` is a fallback for *selection*, not for *fetching*. ## The alternative, and why markup often wins Format choice can also be negotiated server-side, with the server or an image CDN deciding which encoding to return for the same URL. That keeps markup simple, at the cost of moving the logic into infrastructure and complicating caching, since one URL now has several representations. The `<picture>` approach keeps every variant at a distinct, independently cacheable URL and needs no server intelligence at all — which is why static sites and CDN-fronted assets tend to prefer it. ## How to answer Say what `type` gates (decode support, before any fetch), say that selection is first-match in document order so the list is a priority ranking, and finish with the floor: the universally supported file goes on the `<img>`, because that is the candidate with no conditions attached.

  • A source's chosen file returns a 404. Does the browser fall back to the img?
    No. Selection happens once, and the `<img>` is the fallback for *selection*, not for *fetching*. Once a `<source>` matches, the browser commits to that URL, and a 404 or an undecodable file yields a broken image. Nothing re-runs the walk, so a missing AVIF variant is a visible outage for exactly the browsers that support AVIF.
  • Why not just do format negotiation on the server and keep one URL?
    You can, and image CDNs commonly do. The tradeoff is where the logic lives: server-side negotiation keeps markup trivial but makes one URL stand for several representations, which complicates caching and debugging. Markup-side negotiation keeps every variant at its own cacheable URL and requires no server intelligence, at the cost of more verbose HTML.
  • What breaks if you omit type on a source that points at an AVIF file?
    The browser has no decode test to run, so the source passes on its `media` condition alone and gets selected by browsers that cannot decode AVIF. They download the file and fail, producing a broken image. `type` is what turns "try it and see" into a pre-flight check that costs no bytes.

saying these in an interview costs you the question

  • the browser downloads each source until one works
  • later sources override earlier ones, like CSS
  • type converts the image to that format
  • if a source 404s the img src takes over
  • list the safe JPEG first so it always works

context