decoding async on 27 homepages: 704 images ask for it, exactly one asks for sync

The decoding async hint tells a browser the next paint need not wait for an image. Across 1,560 img elements on 27 homepages fetched 2026-09-07, 705 declare a decoding preference and 704 of them say async. One image asks for sync. Nobody writes auto, because auto is already the default.

Implementation8 min read1055 views
decoding async on 27 homepages: 704 images ask for it, exactly one asks for sync

FIELD TEST · 2026-09-07 · 27 homepages · 1,560 img elements · one attribute that never says no

Sample and method: 30 homepages fetched once each on 2026-09-07 with a desktop Chrome user agent, redirects followed, no JavaScript executed, exit node in Japan. Three dropped out — stackoverflow.com, medium.com and www.reddit.com each returned 403 — leaving 27. Every <img> element in the delivered HTML was parsed; images injected later by scripts are not in these counts.

Across 1,560 image elements, 705 carry a decoding attribute and 704 of those say decoding async. Exactly one image on the whole panel asks for sync, and it is a deliberate one. Fifteen of 27 sites use the attribute at all, while 25 use loading — the attribute people reach for first is the one that changes what gets fetched, not the one that changes when it gets painted.

How we measured this

Each saved homepage was scanned for <img> elements, and every element was read for three attributes: decoding, loading and fetchpriority. Values were lowercased. Counts are per element, not per unique image file, so a logo repeated in a header and a footer counts twice — that is the number a browser deals with.

# Count the same three things on your own homepage
curl -sL -A 'Mozilla/5.0' https://example.com/ > home.html
grep -o '<img[^>]*>' home.html | wc -l
grep -o '<img[^>]*>' home.html | grep -c 'decoding='
grep -o '<img[^>]*>' home.html | grep -c 'loading='

The first pass of that scan was wrong, and the way it was wrong is worth handing over. A naive loading= match also hits data-loading=, and www.figma.com ships 20 images carrying both data-loading="true" and loading="eager". The scan read the framework's own bookkeeping attribute, recorded 20 images with a loading value of "true", and produced a tidy-looking table with a value that does not exist in the specification. Anchoring the pattern so it cannot be preceded by a hyphen fixed it. If you run the commands above, the same trap is waiting — grep -c 'decoding=' will happily count data-decoding= too.

Three limits. One request per site, homepage only. No JavaScript ran, so any image a framework mounts on the client is invisible here, and on a panel this heavy with single-page applications that is a real slice of the web being missed. And this counts declarations, not outcomes: we did not measure decode time, paint time or Largest Contentful Paint on any of these pages, so nothing here says whether the attribute helped.

705 of 1,560 images declare a decoding preference

The attribute is present on 45% of image elements and absent on the rest. It splits by site more sharply than by image: four homepages put it on every single image they ship, and twelve homepages never use it once.

On 2026-09-07Count
img elements parsed1,560
Carrying decoding705
Value async704
Value sync1
Value auto0
Carrying loading1,099

Nobody writes auto, and there is a mechanical reason for that: it is already what you get. The HTML Living Standard states that the attribute's "missing value default and invalid value default are both the Auto state" (HTML Living Standard, read 2026-09-07), so typing it changes nothing. That makes this a two-value attribute in practice, and one of those two values appears once in 1,560 elements.

The four sites that decorate every image are nextjs.org (57 of 57), arstechnica.com (60 of 60), www.cloudflare.com (66 of 66) and www.netlify.com (23 of 23). That pattern is what a component library looks like from outside: one image component, one attribute baked into it, applied everywhere without a per-image decision. www.framer.com is the volume leader at 164 of 166.

What decoding async does, and what it does not do

The attribute is a hint about painting, and it has no effect on the network. MDN describes it as a hint about "whether it should perform image decoding along with rendering the other DOM content in a single presentation step that looks more 'correct' (sync), or render and present the other DOM content first and then decode the image and present it later (async)", and gives the practical version in one line: "In practice, async means that the next paint does not wait for the image to decode" (MDN, the img element, read 2026-09-07).

MDN is also candid that the effect is small and hard to see. "It is often difficult to perceive any noticeable effect when using decoding on static <img> elements", it says, because images are fetched and handled independently anyway — though "the blocking of rendering while decoding happens, while often quite small, can be measured — even if it is difficult to observe with the human eye."

Loading decides whether the bytes are fetched at all. Decoding only decides whether the next paint waits. One saves bandwidth, the other saves milliseconds.

That distinction explains the gap between 25 sites using loading and 15 using decoding. Deferring an offscreen image skips a download; MDN describes lazy as avoiding "the network and storage bandwidth required to handle the image until it's reasonably certain that it will be needed". The decoding hint never skips anything. It reorders one step.

decoding async vs loading lazy: how the two actually pair up

Because both attributes sit on the same element, the interesting number is the combination rather than either total. Here is every pairing that appears on the panel.

decodingloadingImages
asynclazy556
nonelazy432
nonenone418
asynceager105
asyncnone43
noneeager5
synceager1

The dominant pairing is async plus lazy, at 556 images. That combination is close to redundant by construction: a lazily loaded image is offscreen, and an offscreen image was never going to hold up the current paint. It costs nothing, and it is what you get when a component sets both by default rather than per image.

The 105 images marked async plus eager are the ones where the hint has something to do. Eager means fetch now, near the top of the page, in the paint the reader is waiting for. Telling the browser not to block that paint on the decode is a real instruction there. And 43 images carry a decoding hint with no loading attribute at all, which means they inherit the eager default without saying so.

Then there are 418 images — 27% of the panel — carrying neither attribute. Those are the browser's defaults on both axes: fetch eagerly, decode however it likes. Ten sites use loading and never decoding; two sites use neither, and both are pages with almost no images (news.ycombinator.com has 2, www.wikipedia.org has 1).

The one image that asks for sync

One element in 1,560 sets decoding="sync", on astro.build, and it is not an accident. The element is the hero background image, and it carries three attributes that agree with each other:

<img src="/_image?href=...HeroBackground...&w=256&h=214&q=10&f=webp"
     alt loading="eager" decoding="sync" fetchpriority="high"
     width="256" height="214">

Read the three together and the intent is legible. Fetch it immediately, give it high priority, and paint it in the same step as everything else rather than letting the page appear without it. The image is requested at 256×214 and quality 10 — a small, heavily compressed placeholder that is cheap to decode, which is what makes asking for a synchronous decode reasonable rather than reckless.

Whether that beats the alternative on this page, we have not measured. It is one image on one homepage, and the honest description is that somebody made a specific choice for a specific element and the rest of the panel did not have to.

What this means for you

This attribute is cheap and low-stakes, which cuts both ways: adding it everywhere is nearly free, and so is the benefit on most of the images that have it. The place it earns its keep is narrow.

  1. Count both attributes on your own homepage with the commands above, then read the ratio. If decoding appears on every image, a component is setting it, and no one chose it per image.
  2. Look at the images above the fold — the ones with loading="eager" or no loading attribute. That set of 105 plus 43 is where the hint changes anything.
  3. Leave async off lazily loaded images if you are trimming markup. It is 556 images on this panel and it is telling the browser not to wait for something it was not waiting for.
  4. Do not write decoding="auto". It is the default for both missing and invalid values, so it is typing without an effect. Zero of 1,560 images here bother.
  5. Reach for sync only with a reason you can say out loud, the way astro.build can: a small, cheap, high-priority image you would rather not have pop in after the layout.

None of this is a ranking input, and treating it as one gets the priorities backwards — the attributes that decide whether an image is fetched, sized and described are worth more, and those are collected in image SEO best practices. How the same panel splits on the fetching side is counted in loading lazy vs eager across 27 homepages. To see the page an engine is handed before any script runs, see what QueryWin reads from a page.

Common questions

How did you measure this?

One GET per homepage on 2026-09-07, desktop Chrome user agent, redirects followed, exit node in Japan, bodies saved to disk. Every <img> element in the delivered HTML was matched and read for decoding, loading and fetchpriority, with attribute patterns anchored so that data- prefixed attributes could not match. Counts are per element. Three of the 30 sites returned 403 and were dropped, leaving 27. No JavaScript ran.

What is the difference between decoding async and loading lazy?

They act at different stages. loading="lazy" defers the download until the image is near the viewport, so it can save bytes that are never spent. decoding="async" does not affect the download at all; it tells the browser the next paint does not have to wait for that image to decode. Pairing them is common — 556 images here do — but on an offscreen image the decoding hint has little left to change.

Does decoding async improve Core Web Vitals?

We did not measure that, and this measurement cannot answer it. What is documented is narrow: MDN says the rendering block during decode is "often quite small" though it "can be measured". On the evidence collected here, all we can say is that 15 of 27 sites think it is worth setting and 12 do not.

Should I add decoding to every image?

Four sites on this panel do exactly that and nothing about it looks harmful. It is also mostly inert on the 556 lazily loaded images that carry it. If a component sets it for free, leaving it is fine; if you are adding it by hand, the images that are fetched eagerly are the ones worth the keystrokes.

Why does nobody use decoding="auto"?

Because it is already in force. The specification makes Auto both the missing value default and the invalid value default, so an image with no attribute and an image with a misspelled attribute both end up in the same state as one that spells auto correctly. Zero of 1,560 images on this panel write it.

decoding async on 27 homepages: 704 images ask for it, exactly one asks for sync