Reduce the bundle size of React Application — Lazy load and Preloading of components and routes
Hello World 🌏
With the increasing number of websites and applications it is becoming a sure thing to provide engaging and seamless web experience to users and in order to provide the buttery smooth experience our application should load lightning fast in users devices. ⚡️ But this is not always the case. In this article I’ll talk about few ways by which we can reduce the initial load time.
If you are working in React then chances are that you are aware of the concept called code splitting and lazy loading. 🕐
If not then Let’s Quickly understand these terms:
Lazy Loading: We can also call it on-demand-loading. An optimisation technique for web and mobile apps. In this technique instead of loading all files at once, first we load the required content and once the required content is loaded, we load other content based on user click or our guess.
Code Splitting: Breaking a large bundle file into smaller chunks.
Why should I care about this? 🙅
By using lazy loading and code splitting we can reduce our application initial load time, which in turn make our application responsive in less time users.
Now, let’s understand how we can use these concepts in our react application.
Let’s setup the stage first.
I’ve created a sample application using
npx create-react-app and created three components Home, Product and Cart.
This is how they look in the UI and there code:
Now, I got this in the network tab when my application got loaded:
The problem with above code is that, Product and Cart page is not required at initial load , but even then they are getting loaded. (write now there sizes are in bytes)
As my application grows 🧶 , so does the resources and libraries and code will grow and this will increase my bundle size. Which in turn increase the bundle size and the initial load time.😵💫
To solve the above problem, We can lazy load 😶🌫️ the routes which are not required🚫 at initial load.
Lazy Loading using
lazyand Code Splitting using
Let’s dive 🏊🏼♂️ into these concepts and get our hands dirty with some code.
In order to achieve lazy loading and code splitting in our application we have to change the we import our components. Below is the demonstration of that change:
Lets quickly see the changes which we have to make:
1. Instead of
imports we have to use
const now. ☝🏽
2. Additional inbuilt function
lazy is being used. ✌🏽
3. This way of importing files is called dynamic imports. 🤟🏽
4. Make sure to move all dynamic imports after doing all normal imports. Otherwise react will throw an error. 🖖🏽
Let us run our application after doing the above change:
Uncaught Error: A React component suspended while rendering, but no fallback UI was specified.
Why did we get this error? 🤹🏽
Ans: Because we are trying to load a component or route whose file is not known to the browser (we have lazy loaded Product Component). Files for lazy loaded components are downloaded on demand they are not download automatically by default.
What is the solution?
We have to use another inbuilt component provided by ReactJS called <Suspense/>.
By using <Suspense/> component we can provide a fallback UI. The UI will be displayed while our component files are being downloaded. And once our component file is downloaded the fallback UI will be removed and our component will be displayed to the user. All this logic is managed by <Suspense/>.
Note: We can have a common fallback UI for all of our Routes or we can have separate Fallback UI for each Routes. This is up to you.
*Input type of
fallback prop is
string or valid JSX or a React Component.
Let us see the behaviour in the browser and in the network tab.
*Whenever I click on Product or Cart (First Time), there JS Files will start downloading and till the download is in progress we will see the fallback UI:
The lazy loading come with a price. 🤦🏽 💰
It adds an unwanted behaviour to our UI application. i.e. the Fallback UI.
There are two scenarios in which user have to see this fallback UI unnecessarily:
1. Fast Network Speed: Users with high speed network will still see this fallback UI for a moment. 🤷🏿
2. Slow network but much time before going to product: There will be cases when a user spent a significant amount of time in Home page before going to any other page. We had the time to download the resources but we haven’t. In such case as well user is going to see the fallback UI. 🙇🏿
We can leverage the concept of
preloading. What we can do is, preload the resources when the browser is idle or browser has bandwidth to do some task in background without hampering the main render and calculation process in the UI.
How can we do that? 🙇🏿
Well we have two ways to solve this problem.
Way 1: Using webpack.
If you have created your app using
create-app command then chances are you already have webpack.
In order to achieve preloading of components you have to insert magical comments in dynamic imports like this:
webpackChunkName is optional while webpackPrefetch is required. With this code, the components files will be pre-fetched when the browser is idle.
The Browser Behaviour 📺 :
The browser we start downloading the files as soon as line number 1 is executed. The import will return a promise which will resolve when downloading is finished.
We are passing the returned promise into the lazy function. By this way the files will be downloaded in background without blocking other operations.
Browser Behaviour 📺:
Wait … 🧘
We are not done yet. 🤸
The above two ways comes up with a problem. 🤺
We don’t have control over the loading of components. The decision is completely on the browser.
Now you may think that how is this is a problem. My components are getting loaded in the background which should I care much about the decision.
It is recommended that we should not load any unwanted resources which will not consumed by the user. By doing this we are saving network cost as well.
For example if someone is just browsing home page and haven’t added something on cart then why should I load cart files.
Cart file should only be loaded when there is something on cart or user has added something to cart.
(Yes, use can click any time to Cart, but in this case the content will loaded on demand basis).
Here is the JS code by using we can control the preloading of components. ( Keep this function in a utility file).
Browser Behaviour — NO .
Now we have the control. Now let us see the developer behaviour:
I hope by using these ways you may improve the UX of your react application. Please do let me know if you have any other way to achieve the same effect. :) or if you liked any of the demonstrated way.