(function(h,o,t,j,a,r){ h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)}; h._hjSettings={hjid:3011767,hjsv:6}; a=o.getElementsByTagName('head')[0]; r=o.createElement('script');r.defer=1; r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv; a.appendChild(r); })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');

JavaScript Optimization: Defer, Async, Code Splitting & More

Published on April 25, 2025
Last Updated on July 1, 2025

Written by

Morgan Frank - Specialist in Page Speed

Key Takeaways

  • 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.

JavaScript optimization

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:

  • “Download this JavaScript file in parallel with parsing the HTML.” (Don’t block parsing)
  • “But wait to execute this script until after the HTML parsing is complete.”
 <script defer src="script.js"></script>

Benefits of defer:

  • Non-Blocking: The script doesn’t block HTML parsing, allowing the browser to build the DOM (Document Object Model) more quickly.
  • Execution Order: Scripts with defer are executed in the order they appear in the HTML, after the DOM is fully loaded. This is important if your scripts depend on each other or on the DOM being ready.
  • Improved Perceived Performance: The user sees content sooner, even if the JavaScript hasn’t finished executing.

When to Use defer:

  • For most scripts that are not essential for the initial rendering of the page.
  • For scripts that depend on the DOM being fully loaded.
  • For scripts that depend on other scripts (the execution order is preserved).
difference between defer and async

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:

  • With async, the script is executed as soon as it’s downloaded, even if the HTML parsing is not finished.
<script async src="script.js"></script>

Benefits of async:

  • Non-Blocking (Mostly): Like defer, the script doesn’t block HTML parsing during download. However, it can interrupt parsing briefly when it executes.
  • Faster Execution (Potentially): The script can execute sooner than with defer, as it doesn’t have to wait for the entire HTML to be parsed.

When to Use async:

  • For scripts that are completely independent and don’t rely on the DOM being fully loaded or on other scripts.
  • For scripts where the execution order doesn’t matter.
  • Common use cases: Analytics scripts, social media widgets (if they don’t need to modify the initial page content).
script execution in async and defer

defer vs. async: A Summary

Feature

Defer

Async

Download

Parallel with HTML parsing

Parallel with HTML parsing

Execution

After HTML parsing is complete

As soon as the script is downloaded

Execution Order

Preserved (in order of appearance in HTML)

Not preserved (can
execute in any order)

DOM Dependency

Safe to use with scripts that depend on DOM

Not safe for scripts that depend on DOM

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:

  • Faster Initial Load: Smaller initial JavaScript bundles mean faster download and execution times, improving metrics like First Contentful Paint (FCP) and Time to Interactive (TTI) .
  • Improved Caching: If you update one part of your application, users only need to download the changed chunk, not the entire bundle.
  • Better Resource Utilization: The browser only loads and executes the code that’s actually needed, saving bandwidth and processing power.

How Code Splitting Works:

Code splitting is typically implemented using module bundlers like:

  • Webpack: A very popular and powerful module bundler with extensive code splitting capabilities.
  • Parcel: A zero-configuration bundler that automatically performs code splitting.
  • Rollup: A bundler that focuses on creating small and efficient bundles, often used for libraries.

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
   });
 });
how javascript bundles

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:

  • Smaller Bundle Sizes: Removing unused code directly reduces the size of your JavaScript bundles.
  • Faster Download and Execution: Smaller bundles load and execute faster, improving page speed.
  • Improved Performance: Less code to parse and execute means better overall performance.

Enabling Tree Shaking:

  • Use ES Modules: Tree shaking works best with ES modules (import/export).
  • Use a Module Bundler: Webpack, Parcel, and Rollup all support tree shaking.
  • Configure Your Bundler: You may need to configure your bundler to enable tree shaking (e.g., in Webpack, you need to use production mode and set optimization.usedExports to true).
javascript tree shaking bundle

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.

Shakeeb Sadikeen

The expert that experts learn from

About Author

Determined to change that, he built RapidLoad — a smart, AI-driven tool that empowers site owners to dramatically improve speed scores, enhance user experience, and meet Google’s Core Web Vitals without needing to touch a single line of code.
Connect with Shakeeb Sadikeen

Table of content