Scripts: async, defer
In the fast-paced world of web development, the efficiency of page loading is a paramount concern. JavaScript, a cornerstone of client-side programming, must be
Where you put a <script> and how you load it has a direct effect on how fast your page becomes usable. By default, scripts can stall the browser mid-render; the async and defer attributes let you tell the browser to download JavaScript in the background instead. This article explains how plain blocking scripts behave, what async and defer change, why module scripts are deferred automatically, and how to choose the right option for each script.
How a plain script blocks parsing
A <script> without async or defer is render-blocking. When the HTML parser reaches the tag, it stops building the page, fetches the script (if it has a src), runs it to completion, and only then resumes parsing the rest of the document:
<p>...content before script...</p>
<script src="big.js"></script> <!-- parsing pauses here until big.js downloads AND runs -->
<p>...content after script (the user can't see this yet)...</p>Two consequences fall out of this:
- The script cannot see DOM elements that appear after it, because they haven't been parsed yet.
- The user stares at a partially built page while the script downloads. On a slow network this is the classic "blank page" problem.
The historical workaround was to put scripts at the very bottom of <body>. async and defer give you a cleaner solution: keep the tag in <head> but stop it from blocking.
Both
asyncanddeferonly affect external scripts — they require thesrcattribute. They are ignored on inline<script>blocks.
Understanding the async Attribute
What is the async Attribute?
When you add the async attribute to a <script> tag, it instructs the browser to download the script in the background without blocking HTML parsing. As soon as the download finishes, the browser pauses parsing, runs the script, and then continues. Because download times vary, async scripts run as soon as they are ready — in an unpredictable order, possibly before the rest of the document has been parsed.
This makes async perfect for independent scripts that don't rely on the DOM or on each other: analytics, ads, and other "fire-and-forget" trackers.
Code Example: Using async
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Async Example</title>
</head>
<body>
<h1>Testing Async</h1>
<script async src="https://example.com/async-script.js"></script>
</body>
</html>Benefits and trade-offs of async
- Non-blocking: HTML parsing continues while the script downloads.
- Runs ASAP: The script executes the moment its download finishes — good for independent scripts.
- No guaranteed order: With several
asyncscripts, whichever downloads first runs first. Never useasyncfor scripts that depend on one another. - May run before the DOM is ready: It can execute before parsing finishes, so don't assume later elements exist.
Leveraging the defer Attribute
What is the defer Attribute?
The defer attribute also downloads the script in the background without blocking parsing. The difference is when it runs: a deferred script is executed only after the HTML document has been fully parsed, and right before the DOMContentLoaded event fires. Deferred scripts also keep their document order — the first defer tag always runs before the second, regardless of which downloads first.
This makes defer the right default for your application code: the whole DOM is guaranteed to exist, and dependent scripts stay in order.
Code Example: Using defer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Defer Example</title>
</head>
<body>
<h1>Testing Defer</h1>
<script defer src="https://example.com/defer-script.js"></script>
</body>
</html>Benefits of Using defer
- DOM Ready: The entire HTML document is parsed before the script runs.
- Order Maintained: Scripts execute in document order, which is essential for scripts that depend on each other.
Module scripts are deferred by default
A <script type="module"> behaves like a deferred script automatically — you don't need to add defer. It is fetched without blocking parsing and runs after the document is parsed, in order. To make a module run as soon as it's ready instead (the async behavior), add async explicitly:
<!-- Deferred automatically; runs after parsing, in order -->
<script type="module" src="app.js"></script>
<!-- Opt into async: runs as soon as it (and its imports) are ready -->
<script type="module" async src="tracker.js"></script>If you are new to modules, see the Modules introduction.
async vs defer at a glance
| Behavior | async | defer |
|---|---|---|
| Blocks HTML parsing? | No | No |
| When does it run? | As soon as it downloads | After parsing, before DOMContentLoaded |
| Execution order | Whichever downloads first | Document order, guaranteed |
| DOM fully available? | Not guaranteed | Yes |
| Best for | Independent scripts (analytics, ads) | App code, dependent scripts |
A simple rule:
- Use
asyncwhen the script stands alone — it doesn't depend on other scripts or on the DOM. - Use
deferwhen the script needs the whole DOM, or when execution order matters.
Practical Example: Script Loading Decisions
Consider loading a utility library (like Lodash) plus your own file that depends on it. Because order matters here, defer is the right choice — both download in the background but run in sequence after parsing:
<script defer src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script defer src="script.js"></script>Here lodash.min.js is guaranteed to run before script.js, and both wait until the page is parsed. Switching these to async would risk script.js running first and failing because Lodash isn't loaded yet.
For loading non-script resources (images, styles) and reacting to their success or failure, see Resource loading: onload and onerror.
Conclusion
Effectively using async and defer attributes in script tags is crucial for modern web development. By understanding and applying these attributes correctly, developers can ensure faster page loads and a better user experience. Asynchronous script loading is about performance optimization and crafting efficient, user-centric web applications.