HTMLScript Loading (async, defer)

Script Loading: async and defer

Where you put a <script> tag—and which loading attribute you give it—has a direct effect on how fast your page becomes visible and interactive. HTML gives you three loading strategies for external scripts: the default (blocking), async, and defer. Picking the right one for each script is one of the highest- impact, lowest-effort performance wins available.
No Attribute: Parser-Blocking
By default, when the HTML parser hits a <script src="..."> tag, it stops everything: it pauses parsing the rest of the document, downloads the script (synchronously, over the network), executes it immediately, and only then resumes parsing the HTML below it.

blocking-script.html

HTML
<head>
  <script src="analytics.js"></script>
  <!-- Parsing of everything below this line waits until
       analytics.js has downloaded AND executed. -->
</head>
Worst case for perceived performance
A blocking script in <head> delays the browser from even seeing the rest of your document, including the <body>. On a slow connection this can add seconds of blank white screen before anything renders.
async: Download in Parallel, Execute ASAP
async tells the browser to download the script in the background without blocking HTML parsing. As soon as the download finishes, though, parsing is paused and the script executes immediately—whenever that happens to be.

async-script.html

HTML
<head>
  <script src="analytics.js" async></script>
  <!-- Parsing continues while analytics.js downloads.
       The moment it finishes downloading, parsing pauses
       and the script runs immediately. -->
</head>
Because execution happens the instant the download completes, the order of multiple async scripts is not guaranteed—whichever finishes downloading first, runs first. That makes async a poor fit for scripts that depend on each other.
Good use cases for async
Independent, order-agnostic scripts like analytics trackers, ad tags, or A/B testing snippets that don't touch the DOM or depend on other scripts.
defer: Download in Parallel, Execute in Order After Parsing
defer also downloads the script without blocking parsing, but it waits to execute the script until after the HTML document has finished parsing(right before DOMContentLoaded). Multiple deferred scripts always run in the order they appear in the document.

defer-script.html

HTML
<head>
  <script src="vendor.js" defer></script>
  <script src="app.js" defer></script>
  <!-- Both download in parallel while parsing continues.
       vendor.js is guaranteed to execute before app.js,
       and both run only after the document is fully parsed. -->
</head>
Good default for application scripts
defer is the right choice for most of your own application JavaScript, especially when scripts depend on each other or need the full DOM to be parsed before they run.
Timeline Comparison

Picture the timeline for a document with one script tag, under each strategy:

timelines.txt

Text
No attribute:
|--- parse HTML ---|--- download ---|--- execute ---|--- parse HTML ---|
                    ^ parsing stops here until script finishes

async:
|--- parse HTML -----------------------|
        |--- download ---|--- execute ---|--- parse HTML resumes ---|
                          ^ parsing pauses only for execution

defer:
|--- parse HTML (uninterrupted) ------------------------------------|
        |--- download (parallel) ---|
                                     execute (after parsing finishes) ^
Comparison Table

Attribute

Blocks parsing?

Download

Execution timing

Order guaranteed?

(none)

Yes

Synchronous

Immediately, blocking

Yes (document order)

async

No

Parallel

As soon as download finishes

No

defer

No

Parallel

After parsing completes

Yes (document order)

Where to Put the Tag
With async or defer, script tags can safely live in <head>—the browser discovers them early and starts downloading immediately without holding up rendering. Without either attribute, the traditional workaround is placing <script> tags right before </body>, so the browser has already parsed and rendered the visible content by the time it hits the blocking script.

placement-comparison.html

HTML
<!-- Modern approach: attribute-based, scripts in <head> -->
<head>
  <script src="app.js" defer></script>
</head>
<body>
  <!-- content -->
</body>

<!-- Legacy workaround: no attributes, scripts at end of <body> -->
<body>
  <!-- content -->
  <script src="app.js"></script>
</body>
Inline scripts ignore async/defer
The async and defer attributes only affect scripts loaded via src. An inline <script>...</script> block always runs immediately, in document order, blocking the parser.
  • No attribute: simplest, but blocks parsing—avoid in <head> for anything non-critical.

  • async: fastest to execute, but unordered—use for independent, standalone scripts.

  • defer: downloads early, executes in order after parsing—the best default for app code.

  • type="module" scripts are deferred by default, without needing the defer attribute.