Contents

Frontend Development › HTML

async and defer

Loading scripts without blocking page rendering.

Also known as: async defer, script async, script defer, defer vs async

By default, a <script> tag blocks the browser: it stops parsing the HTML, downloads the script, runs it, and only then continues. Slow scripts delay the page. The async and defer attributes avoid that.

<script src="app.js"></script>              <!-- blocks parsing while it downloads and runs -->
<script src="app.js" defer></script>        <!-- downloads in parallel, runs after parsing, in order -->
<script src="stats.js" async></script>      <!-- downloads in parallel, runs as soon as ready, any order -->
<script type="module" src="app.js"></script><!-- modules are deferred by default -->
DownloadRunsOrder keptWaits for DOM
(none)blocks the parserimmediatelyyesno
deferin parallelafter the HTML is parsedyesyes
asyncin parallelas soon as downloadednono

Which to use

  • defer for your own app scripts. They can safely use the DOM, and run in the right order. This is the usual default.
  • async for independent scripts that don’t rely on others or the DOM, such as analytics.
  • Neither, only for the rare script that must run immediately and block (some critical inline code).

Gotchas

  • async order is unpredictable. If script B needs script A, don’t make both async.
  • Inline scripts can’t use defer or async. Only external ones with src.
  • Putting scripts at the end of <body> was the old workaround. defer in the head is better, because downloading starts earlier.
  • DOMContentLoaded fires after deferred scripts run. Accessing an element too early gives null.
  • Third-party scripts can still slow things (and run on the main thread), so audit them (render-blocking resources, Core Web Vitals).

See the critical rendering path for why blocking matters.