Understanding when to use async, defer, or both attributes on <script> tags to optimize page loading performance and avoid render-blocking issues.
π‘ What are Script Loading Attributes?
Script loading attributes control how and when JavaScript files are downloaded and executed, preventing render-blocking and improving page performance.
Why it matters: Default <script> tags block HTML parsing and rendering, causing slow page loads and poor user experience.
π§ Core Concepts
Default Behavior (No Attributes)
<!-- β BLOCKS everything --> <script src="app.js"></script>
What happens:
- HTML parsing stops
- Script downloads
- Script executes immediately
- HTML parsing resumes
Impact: Slows First Contentful Paint, blocks rendering.
defer Attribute
<!-- β NON-blocking, executes in order --> <script src="app.js" defer></script>
What happens:
- Download starts in parallel with HTML parsing
- HTML parsing continues uninterrupted
- Script executes after DOM is complete
- Multiple deferred scripts run in order
Best for: Application scripts that need DOM access.
async Attribute
<!-- β NON-blocking, executes immediately when ready --> <script src="analytics.js" async></script>
What happens:
- Download starts in parallel with HTML parsing
- HTML parsing continues
- Script executes as soon as download completes
- May interrupt HTML parsing if downloaded early
Best for: Independent scripts (analytics, ads, widgets).
async defer (Both)
<!-- β οΈ NOT RECOMMENDED --> <script src="script.js" async defer></script>
What happens: async takes precedence, defer is ignored. Same as async only.
β When to Use Each Approach
Use defer for:
- Application scripts that manipulate DOM
- Scripts that depend on each other (maintains execution order)
- Scripts that need DOM to be ready
- Most JavaScript in modern web apps
<!-- β Good: Deferred app scripts --> <script src="react.js" defer></script> <script src="app.js" defer></script> <!-- Runs after React --> <script src="utils.js" defer></script> <!-- Runs in order -->
Use async for:
- Analytics and tracking scripts
- Social media widgets
- Ads and third-party scripts
- Independent utilities that don't need DOM
<!-- β Good: Async independent scripts --> <script src="analytics.js" async></script> <script src="facebook-pixel.js" async></script> <script src="ads.js" async></script>
Never use both:
<!-- β Don't do this --> <script src="script.js" async defer></script> <!-- Same as async only, defer ignored -->
π§ͺ Practical Examples
E-commerce Site Loading Strategy
<!DOCTYPE html> <html> <head> <!-- Critical CSS inline --> <style>body{margin:0}.loading{display:block}</style> </head> <body> <div class="loading">Loading...</div> <!-- β Analytics (async - doesn't need DOM) --> <script src="analytics.js" async></script> <!-- β Framework (defer - needs to load first) --> <script src="vue.js" defer></script> <!-- β App code (defer - depends on framework) --> <script src="app.js" defer></script> <!-- β Utilities (defer - may be needed by app) --> <script src="utils.js" defer></script> </body> </html>
Blog Site Loading Strategy
<!DOCTYPE html> <html> <head> <title>My Blog</title> <!-- Minimal critical styles --> </head> <body> <header>Blog Header</header> <main>Content loads fast...</main> <!-- β Ads (async - independent) --> <script src="ads.js" async></script> <!-- β Comments widget (async - doesn't block content) --> <script src="comments.js" async></script> <!-- β Syntax highlighting (defer - needs DOM ready) --> <script src="prism.js" defer></script> </body> </html>
Single Page Application (SPA)
<!DOCTYPE html> <html> <head> <title>SPA</title> </head> <body> <div id="app"></div> <!-- β All app scripts deferred --> <script src="vendor.js" defer></script> <script src="app.js" defer></script> <script src="routes.js" defer></script> </body> </html>
β οΈ Limitations & Considerations
Execution Order Issues
<!-- β Async scripts run out of order --> <script src="jquery.js" async></script> <script src="app.js" async></script> <!-- May run before jQuery! --> <!-- β Defer maintains order --> <script src="jquery.js" defer></script> <script src="app.js" defer></script> <!-- Runs after jQuery -->
DOMContentLoaded Timing
deferscripts run beforeDOMContentLoadedeventasyncscripts may run before or afterDOMContentLoaded- Default scripts run immediately (block
DOMContentLoaded)
Module Scripts
<!-- Modern ES modules are deferred by default --> <script type="module" src="app.js"></script> <!-- Same as: <script src="app.js" defer></script> -->
Browser Support
defer: IE 10+, all modern browsersasync: IE 10+, all modern browsers- Both attributes:
asynctakes precedence in all browsers
π Performance Comparison
| Attribute | Blocks HTML | Blocks Render | Execution Order | DOM Ready | Use Case |
|---|---|---|---|---|---|
| None | β Yes | β Yes | β Ordered | β Before | Legacy code |
defer | β No | β No | β Ordered | β After | App scripts |
async | β No | β οΈ Maybe | β Unordered | β Unpredictable | Widgets/ads |
π Summary
defer: Use for application JavaScript that needs DOM access and execution orderasync: Use for independent scripts like analytics, ads, and third-party widgets- Never both:
async deferis invalid -asynctakes precedence - Default: Avoid unless you have legacy requirements
Key Rule: If script needs DOM or depends on other scripts β defer. If script is independent β async.
π Related Resources
- Browser Rendering Path (
general/browser_rendering.md) - How scripts affect rendering - Critical Rendering Path (
general/critical_rendering_path.md) - Advanced optimization - ES6 Modules (
js/general-concepts/es6_modules.md) - Modern module loading - Promises (
js/promises/) - Async JavaScript patterns
Test your understanding with 3 quick questions