Script Loading: async and defer
<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
<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
<head>
<script src="analytics.js"></script>
<!-- Parsing of everything below this line waits until
analytics.js has downloaded AND executed. -->
</head><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
<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>async a poor fit for scripts that depend on each other.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
<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>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
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
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
<!-- 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>
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.