Understanding when to use async, defer, or both attributes on <script> tags to optimize page loading performance and avoid render-blocking issues.
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.
<!-- β BLOCKS everything --> <script src="app.js"></script>
What happens:
Impact: Slows First Contentful Paint, blocks rendering.
defer Attribute<!-- β NON-blocking, executes in order --> <script src="app.js" defer></script>
What happens:
Best for: Application scripts that need DOM access.
async Attribute<!-- β NON-blocking, executes immediately when ready --> <script src="analytics.js" async></script>
What happens:
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.
defer for:<!-- β 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 -->
async for:<!-- β Good: Async independent scripts --> <script src="analytics.js" async></script> <script src="facebook-pixel.js" async></script> <script src="ads.js" async></script>
<!-- β Don't do this --> <script src="script.js" async defer></script> <!-- Same as async only, defer ignored -->
<!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>
<!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>
<!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>
<!-- β 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 -->
defer scripts run before DOMContentLoaded eventasync scripts may run before or after DOMContentLoadedDOMContentLoaded)<!-- Modern ES modules are deferred by default --> <script type="module" src="app.js"></script> <!-- Same as: <script src="app.js" defer></script> -->
defer: IE 10+, all modern browsersasync: IE 10+, all modern browsersasync takes precedence in all browsers| 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 |
defer: Use for application JavaScript that needs DOM access and execution orderasync: Use for independent scripts like analytics, ads, and third-party widgetsasync defer is invalid - async takes precedenceKey Rule: If script needs DOM or depends on other scripts β defer. If script is independent β async.
general/browser_rendering.md) - How scripts affect renderinggeneral/critical_rendering_path.md) - Advanced optimizationjs/general-concepts/es6_modules.md) - Modern module loadingjs/promises/) - Async JavaScript patternsTest your understanding with 3 quick questions