- Minimize Javascript: Reduce Javascript file size.
- defer: Download in parallel, execute after HTML parsing, in order. Use for most scripts.
- async: Download in parallel, execute as soon as downloaded. Use for independent scripts.
- Code Splitting: Break up large bundles into smaller chunks loaded on demand.
- Tree Shaking: Remove unused code from your bundles.
JavaScript is essential for creating interactive and dynamic websites, but it can also be a major performance bottleneck if not optimized correctly. Large, unoptimized JavaScript files can significantly delay page load times, leading to a poor user experience and lower search engine rankings. This guide covers four key JavaScript optimization techniques: deferring, async, code splitting, and tree shaking.

1. Deferring JavaScript (defer attribute)
By default, when the browser encounters a <script> tag in your HTML, it pauses the HTML parsing, downloads the JavaScript file, and executes it before continuing to parse the HTML. This is called “parser blocking,” and it can significantly delay the rendering of your page, especially if the JavaScript file is large or the server is slow.
The defer attribute solves this problem. When you add defer to a <script> tag you’re telling the browser:
<script defer src="script.js"></script>
Benefits of defer:
When to Use defer:

2. Asynchronous JavaScript (async attribute)
The async attribute is similar to defer in that it tells the browser to download the JavaScript file in parallel with parsing the HTML. However, there’s a key difference:
<script async src="script.js"></script>
Benefits of async:
When to Use async:

defer vs. async: A Summary
Feature 11218_ba2dfd-ca> |
Defer 11218_3b725f-bc> |
Async 11218_9d907c-5b> |
---|---|---|
Download 11218_c8c378-4d> |
Parallel with HTML parsing 11218_ce34d0-15> |
Parallel with HTML parsing 11218_2e0ba5-a8> |
Execution 11218_0615a3-95> |
After HTML parsing is complete 11218_d46f26-4a> |
As soon as the script is downloaded 11218_898bfa-87> |
Execution Order 11218_5b9307-9c> |
Preserved (in order of appearance in HTML) 11218_cff094-5d> |
Not preserved (can |
DOM Dependency 11218_47f777-5c> |
Safe to use with scripts that depend on DOM 11218_80ac15-ee> |
Not safe for scripts that depend on DOM 11218_703ed4-c5> |
3. Code Splitting
Code splitting is a technique that involves breaking up your large JavaScript bundle into smaller, more manageable chunks. Instead of loading all your JavaScript at once, you only load the code that’s needed for the initial page load, and then load other chunks on demand (e.g., when the user interacts with a specific part of the page).
Benefits of Code Splitting:
How Code Splitting Works:
Code splitting is typically implemented using module bundlers like:
These tools analyze your code and its dependencies and automatically split it into multiple chunks. You can then use dynamic import() statements in your code to load these chunks on demand.
Example (using dynamic import()):
// main.js
const button = document.getElementById('myButton');
button.addEventListener('click', () ⇒ {
// Load the 'module.js' chunk on demand when the button is clicked
import('./module.js')
.then((module) ⇒ {
// Use the module
module.doSomething();
})
.catch((error) ⇒ {
// Handle errors
});
});

4. Tree Shaking (Dead Code Elimination)
Tree shaking is a technique that removes unused code from your JavaScript bundles. It’s like pruning a tree – you remove the dead branches (unused code) to make the tree healthier (smaller bundle size).
How Tree Shaking Works:
Tree shaking relies on the static structure of ES modules (using import and export statements). Module bundlers like Webpack, Parcel, and Rollup can analyze your code and determine which parts are actually being used. Any code that’s not imported and used is eliminated from the final bundle.
Example:
// module.js
export function functionA() {
// ...
}
export function functionB() {
// ...
}
// main.js
import { functionA } from './module.js';
functionA(); // Only functionA is used
In this example, functionB is not used in main.js. During the build process, a module bundler with tree shaking enabled would remove functionB from the final bundle, reducing its size.
Benefits of Tree Shaking:
Enabling Tree Shaking:

Conclusion
Optimizing JavaScript is crucial for achieving fast page load times and a good user experience. By using techniques like deferring, async, code splitting, and tree shaking, you can significantly reduce the size and impact of your JavaScript code, leading to a faster, more responsive, and more efficient website. These techniques work together to ensure that you’re only loading and executing the JavaScript that’s absolutely necessary, when it’s needed.