Improve React Performance using Lazy Loading💤 and Suspense

Lazy loading💤 has come to be one of the optimization techniques widely used now to speed up the load time. The prospect of Lazy Loading helps reduce the risk of some of the web app performance problems to a minimal〽.

In this article, we will look into how to use lazy loading💤 to optimize load time in our React apps.

Lazy loading is an optimization trick💫 whereby we delay the load of an object(image🎦, video🎬, webpage🌎, music file🎶, documents📋) until when it is needed.

When a user opens a webpage, all the contents are downloaded in one fell swoop. Most of the contents may never be interacted with or seen by the user. So why waste precious resources and bandwidth?

To boost the response time of our site, we defer the loading of some non-critical parts of our app at load time. These resources are then loaded when the user accesses part of the page that requires it.

We have SSR(server-side rendered) apps and CSR(Client side rendered) apps.

SSR is the good old web pages built with .HTML and inlined with ASP.NEt, PHP. Each link has a different .HTML file to load

web-app/ - index.html - about.html - faq.html - careers.html

Each page has a different HTML file.

As JS frameworks emerged, the web pages are mashed up into a single js and loaded in one fell swoop. Upon execution in the browser, the requested page is generated by the browser DOM.

All files index.js, about.js, faq.js, careers.js was bundled into one file. Now, when we load the index.html file it drags along the heavily-laden index.js with it. Now, the time⏰ taken to parse through all the codes in the index.js and rendering will be a long wait. If the time taken for each js file to execute is as follows:

index.js 2ms

about.js 10ms

faq.js 5ms

careers.js 9ms

Bundled:

Therefore we will wait for 26ms!! But if we could separate the files in React and load them on demand, we will see that it will take our app 2ms to load and respond.

So instead of downloading the entire code, we can split the bundle into small chunks and dynamically load them at runtime.

So many techniques have been used to code-split React apps. We see them in the next sections.

To code-split, our JS apps, the import() function was introduced. It’s still a proposal, not yet part of JavaScript standard.

This function makes it possible to split our apps into chunks and load them on demand.

The import() takes a string as a parameter. The string is the path of the js file to load.

import('./jsfileto_load.js')

When webpack comes across this, it bundles the file in the path separately.

React.lazy is a new feature added to React when Reactv16.6 was released, it offered an easy and straight-forward approach to lazy-loading and code-splitting our React components.

The callback function of the React.lazy returns a Promise via the import() call. The Promise resolves if the module loads successfully and rejects if there was an error in loading the module, due to network failure, wrong path resolution, no file found, etc.

When webpack walks through our code to compile and bundle, it creates a separate bundle when it hits the React.lazy()and import(). Our app will become like this:

Now, our app is now separated into multiple bundles. When AppComponent gets rendered the mycomponent.bc4567.js file is loaded and the containing MyComponent is displayed on the DOM.

Now, what happens when the file mycomponent.bc4567.js gets loaded, there must be a time lag from when it loaded to when the MyComponent it contains is rendered. What will the user see?

Apparently, your app will seem to freeze for some time. That will be a bad user experience. We need to let the user know that something is happening or loading. In order to do that a new feature associated to React.lazy was added it is the Suspensecomponent.

The Suspense component is used to wrap lazy components to show some fallback content while loading the lazy components.

The component that is being lazily loaded is inserted inside the Suspense component tags. The content to show to the user to tell them that something is going on is placed in the fallback prop of the Suspense component tag.

Knowing where to split your code can be very tricky. Two of the most commonly used methods are route-based and component-based.

Route-based code splitting breaks down an application into chunks per route.

Route-based code splitting is the splitting of your code into bundles that are related to your app’s routes. In SPAs, all route or path is done on the DOM. When u click on a hyperlink the DOM catches the event and runs it through the SPA framework, the current view in the DOM is destroyed while the Component attached to the requested path is created and rendered.

All these components are bundled together in one file and shipped. Now, with route-based chunking, we can split the code into chunks. Each chunk relating to a particular route.

When we load our app /. The App is only rendered. Now, if we navigate to /faq. The faq.5678.js file is fetched via a network and loaded, the containing component, FAQ is rendered.

So, we see that our code was split based on the routes we defined. This doesn’t optimize our app 100%, there are downsides though. But at least we shaved off a sizeable delay time from our app. This another optimization technique associated with code-splitting called prefetching, but that will be in another post.

In as much as they provide many goodies, they also contribute to the poor performance of our apps. Most of the components hide until when the user does something to display it. The footer and the sidenav are never seen until the user either scrolls to the bottom or press the sidenav button. Most cases, the user might not even interact with any of them.

All these are loaded in our app and constitute a time delay on loading. Wouldn’t it be nice to shave off the widgets and load them when the user tries to interact with them?

In this case, component-based splitting helps a lot. All the widgets or components will be bundled separately. Each widget on a separate bundle. It’s up to the web dev to come with a bundling technique but the main thing is to load the widgets on demand.

We see that the ModalComponent is not loaded initially. It is loaded when the user clicks the Open Modal button.

In this post, we saw how we could improve🚀 the performance of our React app by code-splitting and lazy-loading. First, we introduce the dynamic import() function, next, we looked at different techniques using the new 💤React.lazy() and 🚦Suspense.

With these different optimization tricks we learned in this post, we can churn out quality React apps with high performance.

If you have any question regarding this or anything I should add, correct or remove, feel free to comment below and ask me anything! 👏 You can also find me on Twitter, Facebook and always here.