How does "defer" differ from "async" in <script>?
The problem that defer and async solve
When the browser encounters the tag:
<script src="script.js"></script>by default it:
- Stops parsing the HTML,
- Loads
script.js, - Executes it,
- Only then continues building the DOM.
This blocks the page's render, so the user sees a "blank screen" until the JS loads.
That's why the defer and async attributes appeared, to avoid blocking HTML parsing.
defer
<script src="script.js" defer></script>How it works:
- The script loads in parallel with the HTML (asynchronously).
- But it executes only after the HTML has fully finished parsing,
that is, once the DOM is ready, but before the
DOMContentLoadedevent. - Scripts with
deferexecute in the order they're listed in the HTML.
It's great for an application's "main" scripts (for example, main.js, vendor.js).
Example:
<script src="a.js" defer></script>
<script src="b.js" defer></script>Both load in parallel first,
but they execute strictly in order: a.js then b.js, after the DOM is built.
async
<script src="script.js" async></script>How it works:
- The script loads in parallel with the HTML (like
defer), - But it executes immediately after it loads, even if the HTML hasn't finished parsing yet.
- Scripts with
asyncexecute in an unpredictable order, depending on which one finishes loading first.
It's suited for independent scripts (for example, analytics, ads, metrics).
Example:
<script src="a.js" async></script>
<script src="b.js" async></script>b.js can execute before a.js if it loads faster,
even if it appears lower in the HTML.
Comparing defer and async
| Property | defer | async |
|---|---|---|
| Script loading | Asynchronous (in parallel with HTML) | Asynchronous |
| Execution | After HTML fully finishes parsing | Right after loading |
| Execution order | Preserved | Not guaranteed |
| Blocks HTML parsing | No | No |
DOMContentLoaded event | Waits for all defer scripts to run | Doesn't wait |
| Typical scenarios | The site's main logic | External trackers, ads |
A visual example
<!-- HTML is being parsed... -->
<script src="main.js" defer></script>
<script src="analytics.js" async></script>The process:
| Stage | What happens |
|---|---|
| 1 | The browser starts parsing the HTML |
| 2 | Both scripts load simultaneously |
| 3 | analytics.js (async) loads faster, so it executes right away |
| 4 | The HTML finishes parsing |
| 5 | Then main.js (defer) executes |
| 6 | Then DOMContentLoaded fires |
Conclusions
| Attribute | When to use it |
|---|---|
defer | For the site's main JS code that depends on the DOM (for example, initializing the interface) |
async | For independent scripts (analytics, metrics, ads) |
| No attributes | Only when you need to block HTML parsing (rarely needed) |
A tip
If you use a modern bundler (Next.js, Vite, Webpack),
it already adds defer to <script> tags in head for you,
so in modern applications this is already the default best practice.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.