How to Minimize React Bundle Size for Faster Loading Times
Optimising your React Bundle Size
Introduction
Hello ๐, I am Zord Coder a software developer and part-time freelance developer. Recently I was given a task to minimize the React bundle size and optimize the whole application as much as possible. Well, this might seem like an easy job but finding the right solution for your test case can be daunting. I am just gonna mention some of the tried and tested methods we used to reduce our React bundle size from 7 MB (main.js) to ~700kb (main.js) and reduced the Initial loading time from more than the 20s to ~1s in fast 3G.
Importance of fast loading times
A one-second delay in page load time has been shown to cause a 7 per cent loss in conversion and 11 per cent fewer page views. For an online store earning $50,000 a day, that one-second delay adds up to more than $1 million in lost sales each year.
You can understand how big of a change even 1s can make when it comes to loading time in a large-scale business.
Amazon found that every 100 milliseconds of latency cost them 1% in sales.
Below are some of the comparisons I found on Rush.com while researching this article.
if your site loads in 5 seconds, it is faster than approximately 25% of the web
if your site loads in 2.9 seconds, it is faster than approximately 50% of the web
if your site loads in 1.7 seconds, it is faster than approximately 75% of the web
if your site loads in 0.8 seconds, it is faster than approximately 94% of the web
We were able to achieve a very low loading time as of now using the following method and I am gonna show you how can you do that too.
Here is a BrowserStack speed lab report.
We are still working to get a better ranking and will update more optimization methods as we explore them.
React bundle size as a factor
Let's try to understand how the browser loads our React page. From the server, we ship the HTML file and if you clearly observe the HTML file you will see a script tag that is calling your main.js
a chunk that is eventually responsible for everything in your React if you are not using SSR
.
The bigger is the main.js
chunk also main.css
chunk the more time it will take to load and parse. Meaning reducing our React bundle size can eventually reduce the initial page loading time.
Analyzing the React bundle
Analyzing the react bundle should be the entry point in optimizing the website's load time. React bundle contains several components and it's important to identify them and pick out the largest bundle to optimize them.
Tools for analyzing the bundle
Several tools are available for analyzing the React bundle. Webpack Bundle Analyzer and Source Map Explorer are popular tools that can be used to analyze the bundle's components and their sizes.
In this article, we will be taking a glance at some tools, and then we will learn how to use them to Optimize our React bundle size.
Webpack bundle Analyzer
If you are using webpack as a bundler this should be your go-to point for getting a better look at your bundle. It is very easy to install and use.
# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer
for Webpack config:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
// other webpack config options
plugins: [
// other plugins
new BundleAnalyzerPlugin(),
],
};
You might be using Craco to extend the web pack plugins in that case you can use the below code.
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
// other webpack config options
plugins: [
// other plugins
new BundleAnalyzerPlugin(),
],
};
Source Map
Similarly, you can use a generated Source Map to analyze the bundle and I identify the big chunks in your javascript bundle.
Here is a good npm library to achieve that.
Strategies for minimizing React bundle size & Implementation
I have compiled a list of effective strategies that have proven successful for us and can benefit your business as well.
Using smaller libraries
As you already saw above the bigger bundle can drastically increase your React Bundle size and can negatively impact your loading time. Finding a better alternative is always recommended.
Let's see an example :
A popular library for formatting is Date's Momentjs which is approx 4 MB instead you can use another library that satisfies your needs and has a smaller size.
I understand there are gonna be times you don't have any option to change the library in our case we were using Monaco
one whose uncompressed size is 82 MB and we don't have any other alternative to go with. The following methods will help you solve the issue.
Code splitting
Now if the above does not work for you or you want to reduce the React bundle size further you can do some Code Splitting. As the project grows the size of the application grows as well, especially when you are using third-party libraries. We can split that code into chunks and only load the chunk needed for the initial page load; the rest are loaded on a need basis. This doesn't reduce the size of your application but divides the React bundle size into chunks which can be lazy
loaded as we need them.
If you are using React you can achieve it using React.lazy()
but if you want you can use webpack to do this.
// webpack.config.js
module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js',
path: __dirname + '/dist',
},
optimization: {
splitChunks: {
chunks: 'all',
minSize: 10000,
maxSize: 250000,
},
},
};
Additionally, we will explore how to utilize Code splitting and React's Lazy loading to diminish the overall size.
Lazy loading
Lazy loading is a strategy to identify resources as non-blocking (non-critical) and load these only when needed. It's a way to shorten the length of the critical rendering path, which translates into reduced page load times.
Once the code is split we need to asynchronously load them to the browser on a need basis. React has a method that does it out of the box hence we don't need to take the case or split the code and lazy load them.
For example :
import React, { lazy, Suspense } from 'react';
const MyLazyComponent = lazy(() => import('./MyLazyComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<MyLazyComponent />
</Suspense>
</div>
);
}
export default MyComponent;
In the above example, we are Lazy Loading a component. Meaning the Lazy loaded component will not be bundled in the main chunk and will be served by the server when the MyComponent will render.
This significantly reduces the React bundle size and optimizes the overall behaviour if done right. It's best to have a better insight into user behaviour on your website to optimize better using this method.
In my opinion, it's best to start with Route-based splitting and then move on to Component-based splitting.
You can read more about them here.
Tree shaking
Tree shaking is a term commonly used within a JavaScript context to describe the removal of dead code.
It relies on the import and export statements to detect if code modules are exported and imported for use between JavaScript files.
In modern JavaScript applications, we use module bundlers (e.g., webpack or Rollup) to automatically remove dead code when bundling multiple JavaScript files into single files. This is important for preparing code that is production ready, for example with clean structures and minimal file size.
Minification
Minification is a process in which the bundlers try to minimize the code further by removing Whitespaces, and comments, or shortening the long variable names into smaller names.
This can be done easily in Webpack by including a minifier plugin called Terser.
You simply install the plugin
npm install terser-webpack-plugin --save-dev
or,
yarn add -D terser-webpack-plugin
or
pnpm add -D terser-webpack-plugin
Then add the plugin to your webpack
config. For example:
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
};
You can read more about this plugin and its uses here. So that you can tweak it as per your need.
Compression
Compression is a technique that is used mostly by servers to compress the assets before serving them over to the network. This makes a whole lot of difference ass such as 70% of your React bundle size can be optimized using this method if your server already not doing them.
Widely accepted Algorithms are Gzip, Brotli, and Deflate. Where Gzip is accepted by all browsers nowadays.
This method is not related to React but it's standard practice, Below we will be using two examples below:
For express servers:
const express = require('express');
const compression = require('compression');
const path = require('path');
const app = express();
// add compression middleware
app.use(compression());
// serve static assets from the build folder
app.use(express.static(path.join(__dirname, 'build')));
// serve index.html for all remaining routes
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server is listening on port ${port}`);
});
For Nginx Server :
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
You can add the above code to Nginx.config to make all the requests Gziped.
Advance Concepts to minimize React bundle size
SSR
One of the most well-established methods for rendering web content is server-side rendering (SSR). When a user requests a page, SSR generates the complete HTML for the content, which may incorporate information from a datastore or external API.
Simply your React application gets rendered to HTML by the server and then sent to the Browser. By this method, we reduce the huge chunk of Javascript that is needed in the first place to initially load the website.
Once the content is loaded then by the method of hydration
we add the javascript to the rendered HTML and the functionality can take place.
SSR has several benefits like better SEO, FP, FCP, TTI being shorter, and FCP = TTI. But it's a complicated process you can use NextJS which does it out of the box and saves a lot of time.
Server Component [ Experimental ]
Server components are the new talk in the town which is supposed to be Zero-bundled Size. Learn more about it here.
Testing and measuring results
Importance of testing
After all the optimization how are you gonna know if there's a performance boost or reduction in size or if all the work was in vain or if we got the result we wanted?
Here are some ways you can check the performance of your react web app.
Tools for measuring load times
Dev Tools: Almost every famous browser except Safari has this tool inbuilt into the browser itself. You can rich click right now and see inspect right now,
this tool offers several tabs to have better insights but to see the decrease in our React bundle size you should head to the Network tab and filter to JS and CSS like below.
Google Light House: This tool gives a full analysis of your web page and extra info like TTL, FCP, SEO, Performance, etc. This is only available in google chrome only.
Mobile-Friendly Test: This is especially for mobile pages If your client base is mostly mobile users. You should check it out. This is more used for SEO and how your page is seen by the crawlers.
You can read more about SEO and SEO optimization in the below blog.
crack-the-code-of-search-engine-success
Comparing results before and after implementation
Here are some of the screenshots I took while debugging the speed and size of the main chunk.
Before Optimization and after code splitting.
After all the optimization.
The main chunk size is 740kb and the loading time is 2.50s which is in server cold start.
Conclusion
In conclusion, optimizing React bundle size is crucial for faster loading times, which can significantly impact a business's revenue and user experience. By analyzing the bundle, identifying the largest components, and implementing strategies such as using smaller libraries, code splitting, lazy loading, tree shaking, minification, and compression, developers can reduce React bundle size and improve performance. Additionally, advanced concepts such as server-side rendering and server components can further optimize React applications. Continuous monitoring and optimization are also essential to ensure sustained performance improvements. By implementing these strategies and continuously optimizing, developers can create fast and efficient React applications that provide an excellent user experience.
Recap of strategies
A recap of strategies mentioned in the article to minimize React bundle size for faster loading times are:
Analyzing the React bundle
Identifying the largest components
Using smaller libraries
Code splitting
Lazy loading
Tree shaking
Minification
Compression
SSR
Server Component (Experimental)
By implementing these strategies, developers can reduce the initial page loading time and provide a better user experience.
Final thoughts
React Bundle size should be kept as less as possible and we should always try to achieve it in several possible ways. In this article, I have given you generic strategies which apply to all React apps to reduce the React bundle Size.
However, if you have a huge codebase this might not solve all your problems in the next blog I will be talking about the Optimization you can do which is based on the user behaviour and how they interact with your website.