Reducing JavaScript is a critical aspect of developing modern websites and a key aspect in terms of overall page efficiency.
As software engineering evolves, there is always a push for faster, more efficient sites with an overall smaller JavaScript payload. Unused JavaScript adds unnecessary bloat to your web applications and slows down overall performance.
In this article, you will learn about a series of patterns and tips that you can use to reduce this bloat by removing unused JavaScript, helping you save time, optimize performance, and improve efficiency.
What is unused JavaScript, anyway?
In the simplest terms, unused JavaScript — often called dead code — is any code that your web app doesn’t use or need, but exists in the final JavaScript bundle that you ship to the browser. This dead code sits dormant and increases the overall size of your JavaScript bundle, which can impact performance.
There could be many reasons for unused code in your JavaScript bundle.
The most obvious reason is that you might have added code that you no longer need, but you forgot to remove it in the final bundle. These could be functions, classes, or variables that are no longer being executed, called, or used anywhere in your app.
Another reason could be unused dependencies. In other words, you might be using a third-party JavaScript dependency in your code that you are not using. Even worse, those dependencies could have their own unused JavaScript, further adding to the unnecessary bloat in your project.
Removing unused JavaScript
There are a couple of ways you can remove unused JavaScript from your web apps. These tips and patterns will help you ship more robust and efficient JavaScript bundles to the web, whether you are using plain old JavaScript or any dedicated libraries or frameworks like React, SolidJS, or Vue.js.
Code splitting
Code splitting is a technique used to split JavaScript code into smaller, more manageable chunks. You can then load these chunks on demand or in parallel network requests, meaning you don’t have to load your entire JavaScript bundle every time — only what’s necessary.
Imagine you had a single JavaScript bundle like the following: <script src=”mainbundle.js”></script> // 60kb file
You can split it into smaller chunks that can be downloaded only when needed: <script async defer src=”chunk1.js”></script> // 30 kb file <script async defer src=”chunk2.js”></script> // 30 kb file <script async defer src=”chunk3.js”></script> // 30 kb file
This strategy reduces the overall network load on the main thread responsible for initializing and downloading JavaScript scripts. If you’re using a JavaScript library or framework like Next.js or Vue, you don’t need to do this manually — most modern JavaScript frameworks support code splitting by default.
Tree shaking
Tree shaking refers to eliminating dead code — i.e., the unwanted JavaScript that is not being used by your app. Many popular bundlers, such as Webpack, Rollup, or Vite, use a tree-shaking approach while building the JavaScript chunks that you ship to the browser.
To ensure tree shaking in your bundle, always use the modern ES6 syntax in your JavaScript components — that is, import and export syntax: // default import import Navbar from ‘Components’ // named import import { Navbar } from ‘Components’ // default export export default Navbar // named export export { Navbar }
The modern ES6 syntax helps your bundler identify dead code, similar to how ESLint points out if there is an imported component but it’s not being consumed anywhere.
JavaScript minification
Less JavaScript in your bundle means less time needed for a browser to download the bundle. To ensure your JavaScript is as light as it can get, always minify it before shipping.
Minifying JavaScript will trim out whitespaces, syntax highlighting, comments, and other parts of your code that you don’t need in the final production build. These unnecessary snippets take up space in the bundle as dead code.
Even simple-looking JavaScript code can be compressed and modified. Here’s an example of simple JavaScript code before minification: // add function const add = (a, b) => { return a + b } // call add function add(3, 4)
Here’s how this code looks after minification: const add=(d,a)=>d+a;add(3,4);
Imagine the effect minifying JavaScript would have on large-scale bundles!
A small tip that can save your network bandwidth significant amounts of time is always loading JavaScript asynchronously.
One way you can do this is by adding async and defer to your JavaScript scripts. This will automatically handle JavaScript downloading and won’t delay or block your HTML from parsing or rendering while JavaScript is being loaded.
async and defer attributes handle JavaScript script downloading and execution in a slightly different order. You can pick what is best for your project.
Dynamic imports
Dynamic imports are now possible in plain JavaScript thanks to ES6 modules. These are particularly helpful when you want to load JavaScipt modules or scripts conditionally. Here’s how you would write dynamic imports: import(‘./utility.js’) .then((module) => { // use utility code here }) .catch((error) => { // catch errors });
This strategy enables us to request a JavaScript bundle after certain conditions are met, as opposed to importing every JavaScript component at the top of the file. Consider this code snippet, where the module is loading only after attaching the event listener
Lazy loading
Lazy loading in JavaScript is a simple but quite useful pattern. When done correctly, lazy loading can help you conserve network bandwidth. The fundamental rule is to load only those JavaScript modules that are needed at the current time.
One pattern you can follow is to always load JavaScript on the viewport height. Suppose you had a very extensive list of users. You might not want to load the information of, say, the 300th user, as this information isn’t needed or even visible within the current viewport.
There are some really brilliant libraries out there for this particular use case, which delegate JavaScript modules to only load when a specified user — such as the 300th — reaches the current viewport.
Lazy loading is not just limited to lists. Assets such as images, videos, a large number of nodes, and more can be lazy loaded as well. For example, you can put up a placeholder before the actual image and its associated JavaScript gets downloaded by the browser.
These patterns not only help you with shipping less JavaScript code but improve user experience by a huge margin as well.
Reducing JavaScript is a critical aspect of developing modern websites and a key aspect in terms of overall page efficiency.
As software engineering evolves, there is always a push for faster, more efficient sites with an overall smaller JavaScript payload. Unused JavaScript adds unnecessary bloat to your web applications and slows down overall performance.
In this article, you will learn about a series of patterns and tips that you can use to reduce this bloat by removing unused JavaScript, helping you save time, optimize performance, and improve efficiency.
What is unused JavaScript, anyway?
In the simplest terms, unused JavaScript — often called dead code — is any code that your web app doesn’t use or need, but exists in the final JavaScript bundle that you ship to the browser. This dead code sits dormant and increases the overall size of your JavaScript bundle, which can impact performance.
There could be many reasons for unused code in your JavaScript bundle.
The most obvious reason is that you might have added code that you no longer need, but you forgot to remove it in the final bundle. These could be functions, classes, or variables that are no longer being executed, called, or used anywhere in your app.
Another reason could be unused dependencies. In other words, you might be using a third-party JavaScript dependency in your code that you are not using. Even worse, those dependencies could have their own unused JavaScript, further adding to the unnecessary bloat in your project.
Removing unused JavaScript
There are a couple of ways you can remove unused JavaScript from your web apps. These tips and patterns will help you ship more robust and efficient JavaScript bundles to the web, whether you are using plain old JavaScript or any dedicated libraries or frameworks like React, SolidJS, or Vue.js.
Code splitting
Code splitting is a technique used to split JavaScript code into smaller, more manageable chunks. You can then load these chunks on demand or in parallel network requests, meaning you don’t have to load your entire JavaScript bundle every time — only what’s necessary.
Imagine you had a single JavaScript bundle like the following: <script src=”mainbundle.js”></script> // 60kb file
You can split it into smaller chunks that can be downloaded only when needed: <script async defer src=”chunk1.js”></script> // 30 kb file <script async defer src=”chunk2.js”></script> // 30 kb file <script async defer src=”chunk3.js”></script> // 30 kb file
This strategy reduces the overall network load on the main thread responsible for initializing and downloading JavaScript scripts. If you’re using a JavaScript library or framework like Next.js or Vue, you don’t need to do this manually — most modern JavaScript frameworks support code splitting by default.
Tree shaking
Tree shaking refers to eliminating dead code — i.e., the unwanted JavaScript that is not being used by your app. Many popular bundlers, such as Webpack, Rollup, or Vite, use a tree-shaking approach while building the JavaScript chunks that you ship to the browser.
To ensure tree shaking in your bundle, always use the modern ES6 syntax in your JavaScript components — that is,
import
andexport
syntax: // default import import Navbar from ‘Components’ // named import import { Navbar } from ‘Components’ // default export export default Navbar // named export export { Navbar }The modern ES6 syntax helps your bundler identify dead code, similar to how ESLint points out if there is an imported component but it’s not being consumed anywhere.
JavaScript minification
Less JavaScript in your bundle means less time needed for a browser to download the bundle. To ensure your JavaScript is as light as it can get, always minify it before shipping.
Minifying JavaScript will trim out whitespaces, syntax highlighting, comments, and other parts of your code that you don’t need in the final production build. These unnecessary snippets take up space in the bundle as dead code.
Even simple-looking JavaScript code can be compressed and modified. Here’s an example of simple JavaScript code before minification: // add function const add = (a, b) => { return a + b } // call add function add(3, 4)
Here’s how this code looks after minification: const add=(d,a)=>d+a;add(3,4);
Imagine the effect minifying JavaScript would have on large-scale bundles!
JavaScript minification is easier than you might think. You can pick from a number of JavaScript minification tools available online, such as Terser, Ugligy, babel-minify, and more.
Load JavaScript asynchronously
A small tip that can save your network bandwidth significant amounts of time is always loading JavaScript asynchronously.
One way you can do this is by adding
async
anddefer
to your JavaScript scripts. This will automatically handle JavaScript downloading and won’t delay or block your HTML from parsing or rendering while JavaScript is being loaded.async
anddefer
attributes handle JavaScript script downloading and execution in a slightly different order. You can pick what is best for your project.Dynamic imports
Dynamic imports are now possible in plain JavaScript thanks to ES6 modules. These are particularly helpful when you want to load JavaScipt modules or scripts conditionally. Here’s how you would write dynamic imports: import(‘./utility.js’) .then((module) => { // use utility code here }) .catch((error) => { // catch errors });
This strategy enables us to request a JavaScript bundle after certain conditions are met, as opposed to importing every JavaScript component at the top of the file. Consider this code snippet, where the module is loading only after attaching the event listener
Lazy loading
Lazy loading in JavaScript is a simple but quite useful pattern. When done correctly, lazy loading can help you conserve network bandwidth. The fundamental rule is to load only those JavaScript modules that are needed at the current time.
One pattern you can follow is to always load JavaScript on the viewport height. Suppose you had a very extensive list of users. You might not want to load the information of, say, the 300th user, as this information isn’t needed or even visible within the current viewport.
There are some really brilliant libraries out there for this particular use case, which delegate JavaScript modules to only load when a specified user — such as the 300th — reaches the current viewport.
Lazy loading is not just limited to lists. Assets such as images, videos, a large number of nodes, and more can be lazy loaded as well. For example, you can put up a placeholder before the actual image and its associated JavaScript gets downloaded by the browser.
These patterns not only help you with shipping less JavaScript code but improve user experience by a huge margin as well.
Written by Muhammad Talha Waseem
Recent Posts
Recent Posts
Business Rules Management with Drools: An Introduction
Reactive Programming in Java
Integrating DeepL Translation API with Java
Archives