Improve your app's performance with React.lazy and Suspense
We've all been there. You're working on a single page application and everything's going smoothly. You are used to how the app feels, handles sessions, and renders data. Then one day you decide to add that feature you were thinking about for quite some time. It gets merged into master, and now your once stable application starts crashing. Why is that? It is because there are so many moving pieces to managing an application. The newest features are often dependent on other features which can't be loaded until the initial load completes.
Additionally, from a user’s perspective the speed of the app is really crucial. If your app slows down and doesn't load as quickly as it should, then users will get frustrated and will leave your app and go find another one that works quickly. React.lazy and Suspense can help make it easier to load only the most relevant of your components, making them more efficient and avoiding unnecessary requests from your users.
When apps start to grow you can easily end up including too many components, third-party libraries making it heavier and slow to load. One of the solutions is code splitting. One way to manage this is code-splitting - where you split your initial bundle into multiple bundles and load them at runtime when needed. Code Splitting is supported by bundlers like Webpack, Rollup, and Browserify which can create multiple bundles and dynamically load at runtime.
Code-Splitting combined with lazy loading can help improve the performance of your app as well as the user experience by loading only the required component instead of loading everything during the initial load.
React.lazy
React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component. (Source: reactjs.org)
React.lazy lets you render dynamic imports as a component. It makes it easy to code-split react applications at component level.
The React.lazy function provides a built-in way to separate components in an application into separate chunks of JavaScript with very little legwork. You can then take care of loading states when you couple it with the Suspense component.
Suspense
Suspense allows us to show some fallback content (such as a loading indicator) while we’re waiting for the lazy component to load. Suspense can be placed anywhere above the lazy component.
We can wrap multiple lazy components under a single Suspense. fallback prop in the Suspense component accepts React elements that will be rendered until the lazy component gets loaded.
Example
import React from "react";
import PropTypes from "prop-types";
import Lottie from "react-lottie";
export default class LottieView extends React.Component {
render() {
const { styles, animationData } = this.props;
const options = {
loop: true,
autoplay: true,
animationData,
rendererSettings: {
preserveAspectRatio: "xMidYMid slice"
}
};
return (
<Lottie options={options} styles={styles} />
);
}
}
In this example, we used the react-lottie module which is used to load animation JSON files in the React application.
Before adding React.lazy and Suspense
These are the module download time and bundle size without any optimization, 1.chunck.js took 4.22s and its size is 551kb.
So let's include React.lazy and Suspense in the above code and check the results
import React, { Suspense, lazy } from "react";
import PropTypes from "prop-types";
const Lottie = lazy(() => import("react-lottie"));
export default class LottieView extends React.Component {
render() {
const { styles, animationData } = this.props;
.
.
.
return (
<Suspense fallback={<div>Please wait</div>}>
<Lottie options={options} styles={styles} />
</Suspense>
);
}
}
After adding React.lazy and Suspense
After adding React.lazy and Suspense, bundle size reduced to 442kb from 551kb and loading time also reduced. After adding lazy, code bundlers exclude the lazy component in the initial bundle and make it a separate bundle then it gets downloaded on demand.
How and where to apply code splitting to your React application?
Begin at the route level. Routes are the simplest way to identify points of your application that can be split. The React docs show how Suspense can be used along with react-router.
Identify any large components on a page on your site that only render on certain user interactions (like clicking a button). Splitting these components will reduce your JavaScript payloads.
Consider splitting anything else that is offscreen and not critical for the user.
In this article, we looked at an example of how code splitting can be combined with Suspense to implement a fairly simple but powerful feature. Code splitting can be combined with a lot of other features like Server Side Rendering (SSR) to reduce the initial payload size and make the app load faster. However, not all features are appropriate for code splitting - security oriented features being one of those that should not be split out. While code splitting and lazy loading will help us reach these performance improvements, it is important to consider the implications that these changes might have from a user experience perspective.