How to use React.lazy and Suspense for components lazy loading


By Boris Sever
June 2019

Hire Boris

React 16.6 brought code-splitting to a new level. You can now load your components when it’s really needed without installing additional libraries.

What are code-splitting and lazy loading?

Webpack defines code-splitting as:

“Technique of splitting your code into various bundles which can then be loaded on demand or in parallel”. Source

Another way to say: “loading on demand or in parallel” is lazy-loading. Opposite of lazy-loading is eager-loading. Here everything is loaded no matter if you use it or not.

Why would we use code-splitting and lazy loading?

Sometimes we have to introduce a big chunk of code to cover some functionality. This code can be importing 3rd party dependency or writing it on our own. This code then affects the main bundle’s size.

Downloading a few MBs is a piece of cake for today’s internet speed. We still have to think about the users with a slow internet connection or using mobile data.

How was it done before React 16.6?

Probably the most popular library for lazy loading of React components is react-loadable.

It’s important that reactjs.org still recommends react-loadable if your app is rendered on the server. Source

react-loadable is actually pretty similar to the new approach by React. I will show this in the following demo.

Is anything needed for setup?

Let's see what reactjs.org has to say about it:

“If you’re using Create React App, Next.js, Gatsby or a similar tool, you will have a Webpack setup out of the box to bundle your app. If you aren’t, you’ll need to setup bundling yourself. For example, see the Next.js, Installation and Getting Started guides on the Webpack docs.“ - reactjs.org

Ok, so Webpack is required, which handles dynamic imports of the bundles.

The following demo is generated using Create React App. And in that case, Webpack is already configured and we’re ready to go.

DEMO

For this demo, we will use react.pdf. react-pdf is an awesome library used for creating PDF files on the browser, mobile, and server. We could generate a PDF on the server, but if we would rather do it on the client side, it comes with a cost: bundle size.

I’m using Import cost extension for Visual Studio Code to see the sizes of the libraries used. Let's say our requirement is to generate a PDF file when a user clicks on the button.

Now, this is a simple form with only one use case. Try to imagine a huge web app where this is a fraction of possibilities. Maybe this functionality is not used very often by the users.

Let’s put ourselves into that situation. PDF generation isn’t used very often and it doesn’t make sense to load the whole code for every page request.

I’ll try to show how we can develop a solution with lazy loading and without it.

Eager VS lazy loading showcase

For both cases, we will use one component which imports dependencies from react-pdf and renders a simple PDF document.

Nothing spectacular going on here. We import PDFViewer, Document, Page, Text, View from react-pdf. These are all used in render method of PDFPreview component.

PDFPreview receives only one prop called title. As the name implies, it is used as a title in a newly generated PDF file.

pdfStyles.js looks like this:

Eager loading

Let’s first see how the parent component without lazy loading could look like:

which renders the following view in the browser:

Let's go through the code together:

On line 2 we import PDFPreview component.

On line 6 we initialize the state with default values. name is a field used as a title in the PDF file, while field PDFPreview is a boolean which shows or hides PDFPreview.

Now, let's jump to render method and check what will be rendered.

On line 19 and 25 we render an input and a button. When user types into the input, name in the state is changed.

Then when a user clicks on the “Generate PDF ”, show PDFPreview is set to true. The component re-renders and shows the PDFPreview component.

Even though we use PDFPreview only on user click, all code related to it is included in the app bundle:

This is a development environment. In production, the sizes would be significantly smaller. Still, we’re not splitting the code optimally.

Lazy Loading

We’ve only made small changes and let's go through them.

Line 2 is replaced with: const LazyPDFDocument = React.lazy(() => import("./PDFPreview"));

Let's see what the React docs say about 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. - reactjs.org

On line 27 we use Suspense, which must be a parent of a lazy-loaded component. When showPDFPreview is set to true, LazyPDFDocument is starting to load.

Until the child component is resolved, Suspense shows whatever is provided to fallback prop.

The end result looks like this:

We can see 0.chunk.js weights significantly less than before and 4.chunk.js and 3.chunk.js are loaded on button press.

Conclusion

Every time we are introducing a new dependency into our project, our responsibility is to evaluate its cost and check how it affects the main bundle.

Then we have to ask is this functionality going to be used rarely and can we load it on demand without sacrificing the user experience.

If the answer is yes, then React.Lazy and Suspense really help us with that task.

Thank you for reading! Please share it with anyone who might find it useful and leave feedback

Author: Boris Sever, Web Technology Consultant

Boris is a passionate Full-stack developer. He combines his coding skills with management and lead several teams during his career. Switching between back- and front-end, he adopted new stacks quickly and developed an eye for detail.

Hire Boris