← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Script Loading (defer, async, type=module)

Frontend Intermediate
debt(d3/e1/b3/t7)
d3 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'default linter catches the common case' (d3) — Lighthouse and PageSpeed Insights flag render-blocking scripts as a standard audit, surfacing the issue routinely in any perf check.

e1 Effort Remediation debt — work required to fix once spotted

Closest to 'one-line patch' (e1) — quick_fix is literally adding a defer attribute to the script tag; trivial per-tag change.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3) — script loading choices live in HTML templates/layout; affects page load behavior broadly but doesn't shape architecture, though wrong choices proliferate across templates.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7) — misconception explicitly states devs treat async and defer as interchangeable, but async breaks execution order for dependent scripts causing intermittent race conditions; also defer/async are silently ignored on inline scripts, contradicting expectations.

About DEBT scoring →

Also Known As

defer script async script script defer script async render blocking script javascript loading strategy

TL;DR

How and when browser downloads and executes JavaScript — defer, async, and type=module control whether scripts block HTML parsing and in what order they run.

Explanation

By default, a <script src='...'> in <head> pauses HTML parsing until the script downloads and executes — blocking the entire render pipeline. Three attributes change this: async downloads in parallel and executes immediately when ready (order not guaranteed); defer downloads in parallel and executes after HTML is fully parsed, in source order; type='module' is deferred by default, scoped to the module, and supports static import/export. The rule of thumb: defer for app scripts that need the DOM or depend on each other, async for fully independent scripts like analytics. Inline scripts ignore both attributes — they always block. Placing scripts at the end of <body> is the old workaround; defer is the modern equivalent with better performance characteristics.

Common Misconception

async and defer are interchangeable — async executes as soon as downloaded, breaking execution order for dependent scripts; defer preserves source order and runs after DOM parsing, making it safe for scripts that depend on each other.

Why It Matters

A single render-blocking script in <head> delays First Contentful Paint by the full download and parse time of that script — on a slow connection this is hundreds of milliseconds of blank page before any content appears.

Common Mistakes

  • <script> in <head> without defer or async — blocks HTML parsing and delays render.
  • Using async on scripts that depend on each other (jQuery then a jQuery plugin) — causes intermittent race condition failures.
  • defer or async on inline scripts — both attributes are silently ignored; inline scripts always block.
  • Not adding type='module' to ES module scripts — modules are deferred by default and enable import/export and tree-shaking.

Avoid When

  • Do not use async on scripts that depend on other async scripts — execution order is not guaranteed.
  • Do not add defer or async to inline scripts — they are ignored and the script will still block.

When To Use

  • Use defer for any script that needs the DOM or has dependencies on other scripts.
  • Use async for analytics, ads, or chat widgets that are fully self-contained.
  • Use type='module' for ES module-based projects to get deferred execution and import/export support.

Code Examples

✗ Vulnerable
<!-- Blocks render — browser stops parsing HTML until jQuery downloads and runs -->
<head>
  <script src="jquery.js"></script>
  <script src="app.js"></script>
</head>
✓ Fixed
<!-- defer: parallel download, executes in order after DOM ready -->
<head>
  <script src="jquery.js" defer></script>
  <script src="app.js" defer></script>
</head>

<!-- async: only for fully independent scripts with no dependencies -->
<script src="analytics.js" async></script>

<!-- type=module: deferred, scoped, supports import/export -->
<script type="module" src="app.mjs"></script>

Added 6 Apr 2026
Views 33
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 1 ping S 0 pings M 2 pings T 0 pings W 0 pings T 2 pings F 0 pings S 2 pings S 0 pings M 2 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Google 5 Scrapy 5 Ahrefs 3 SEMrush 3 Meta AI 2 Claude 2 Bing 2 ChatGPT 1 Perplexity 1 Sogou 1 PetalBot 1
crawler 21 crawler_json 5
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Add defer to all app scripts in <head>; use async only for independent analytics/tracking scripts; never leave bare <script src='...'> in <head>
📦 Applies To
html web
🔍 Detection Hints
<script src without defer or async in <head>
Auto-detectable: ✓ Yes lighthouse pagespeed-insights webhint
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line


✓ schema.org compliant