Reference guide · html-css-js · Published 2026-08-16 · 4 min read

JavaScript defer and async

JavaScript defer vs async: execution order explained, when to use each, and how they interact with module scripts.

Flat editorial illustration showing wireframe bricks and translucent layer panels assembling into a small browser window outline with dual panes.
Illustration: this article at a glance.

The two attributes, in one picture

Without any attribute, a <script src> is blocking: the browser stops parsing HTML, downloads the script, executes it, and only then continues parsing. That is the cause of many "page is blank while script loads" cases.

Editorial close-up illustration showing wireframe bricks and translucent layer panels assembling into a small browser window outline with dual panes.
Illustration: a closer look at the technique described above.

With defer, the browser downloads in parallel with parsing, then executes in document order *after* the document has finished parsing (DOMContentLoaded comes after). Deferred scripts preserve order.

With async, the browser downloads in parallel and executes as soon as it arrives, interrupting parsing at that moment, in whatever order the network returns. Asynchronous scripts do not preserve order.

AttributeParsingExecution orderFires relative to DCL
noneblocks parsingin order, at parse timebefore DCL
asynccontinues during downloadwhichever finishes firstpossibly before DCL
defercontinues during downloadtrue document orderafter DCL

Both async and defer stop the renderer-bottleneck download blocking; the difference is the execution timing and the ordering guarantee.

When each is right

The render-blocking resources article explains why render-blocking matters to the LCP metric; defer and async are the lever that turns blocking scripts into non-blocking ones.

The order rules that bite

  1. Deferred scripts run in document order. defer a then defer b always executes a before b, even if b finishes downloading first.
  2. Async scripts run in the order they finish. Two async scripts promise nothing about order.
  3. Deferred scripts run before DOMContentLoaded. They do *not* run after the page is fully interactive if DCL is already over.
  4. When a script has both async and defer, async wins in browsers that honor both (they are mutually exclusive semantics).

Module scripts and the modern default

The word "defer" is largely assumed for ES modules (<script type="module">): module scripts behave like deferred scripts (downloaded in parallel, executed after parsing, in order) and add stricter correctness (static import resolution, strict mode, top-level await forbidden). For new work, shipping modules and letting the module semantics be your ordering is closer to an explicit design than mixing files.

A modern pattern that works well:

The debugging angle

When a script "doesn't work", the first question is which timing bucket it is in. A badly-timed init script (runs before the element it wires exists) looks identical to a genuine bug; the HTML/CSS/JS order article steps through isolating it. When a third-party snippet "fires late", usually it is async and you asked for that: it runs when the network delivers, not when the page finished. And when a page is slow on LCP specifically, an un-deferred script in the <head> is a top suspect, the waterfall article will show it as a block.

Prevention

Adopt the default "defer local scripts, async third-party" and only use the plain attribute for code that must block the head. Audit the page with the Network panel's "Script" filter; you should never see a blocking script in <head> unless it has to be there, and a module-based codebase makes the ordering question disappear entirely.

Need a website built, fixed, optimised, migrated or replaced?

This technical resource is written by CSMBAC, a small design and development studio. If you would rather hand the problem to a professional, the website service page explains how we build enquiry-ready websites.

Explore website services