Faster React Page Loads With Lazy and Suspense
Marius Reimer
Posted on July 28, 2020
Third-party libraries, images and huge amount of static data can all influence your application bundle size. This can cause unexpected high loading times, which may lead to a bad first site impression. React.Lazy and React.Suspense are common techniques (as of mid 2020), to perform code splitting for bundle size reduction and speeding up page load. In this article I want to show quick you may add code splitting to your React application, highlighting the differences in performance (Lighthouse benchmark/check).
The base application
The idea is that we have a React component, that just displays some static data from a JSON file. I have chosen the programming-quotes-api in order to have some data that makes sense. This data is not being fetched at runtime, but put into a local JSON file, which means it will be bundled into the application. To make the data a bit bigger, I have duplicated its content.
The app boilerplate was created by the common create-react-app
tool as described here. From there on, I have created a React component, call it VeryBigJokesList
, that displays the static content.
import React from 'react'
import preDefinedJokes from './preDefinedJokes.json'
const VeryBigJokesList = ({ jokes = preDefinedJokes }) => {
if (!Array.isArray(jokes)) {
return <p>No jokes found.</p>
}
return (
<ul>
{
jokes.map((joke, i) => <li key={i}>{joke && joke.en}</li>)
}
</ul>
);
}
export default VeryBigJokesList;
The non-lazy (eager) case
Usually, I would just import the VeryBigJokesList
component and render it in the App
component, created by the boilerplate.
import * as React from 'react';
import VeryBigJokesList from './VeryBigJokesList';
function App() {
return (
<div className="App">
<header className="App-header">
<div style={{ maxWidth: 600 }}>
<VeryBigJokesList />
</div>
</header>
</div>
);
}
export default App;
This causes to the user load all content from VeryBigJokesList
when loading App
, since it will be “placed” in the same final bundle. When building the application via yarn build
or npm run build
, you will see a list of all bundled files of your application.
As you can see, the main bundle is yellow highlighted, indicating that its size may be too big. This makes sense, since the JSON data that VeryBigJokesList
includes is roughly this size. When running a Lighthouse performance check, you should also see some loading specific issues.
The lazy case
When planning to use React.Lazy, you mostly need to consider the fact that VeryBigJokesList
is being exported via export default
and is put as a child (of any depth) of a React.Suspense component. Suspense allows you to render a fallback component (like a loading indicator), while its content is loading.
import * as React from 'react';
const VeryBigJokesList = React.lazy(() => import('./VeryBigJokesList'));
function App() {
return (
<div className="App">
<header className="App-header">
<div style={{ maxWidth: 600 }}>
<React.Suspense fallback={<p>Loading list...</p>}>
<VeryBigJokesList />
</React.Suspense>
</div>
</header>
</div>
);
}
export default App;
Adjusting VeryBigJokesList
to load lazily was rather simple. If everything worked well, you should see a loading text, before the list is displayed. When building the application, you should also see a difference.
The main bundle size has decreased dramatically, since the static JSON content has moved to a different chunk of the bundle. When running a Lighthouse performance check, you should see a difference in loading times.
Originally published at https://mariusreimer.com on July 26, 2020.
Posted on July 28, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.