Introduction
When developing large React applications, we typically use many images and videos, install third-party packages/libraries, make API calls, and do a variety of other things. That naturally increases the time it takes to load our application and results in a massive bundle size, which contributes to a poor user experience. That's where lazy loading comes into place. It enables us to load the content just in time, right before it will be displayed in the application.
We can avoid pre-loading content while it's still out of view, and focus all of the resources on the content that is in the view.
In this guide, we'll look at how to use
React.lazy()
andReact.Suspense
to implement lazy loading and code splitting functionality that allows us to handle code splitting without the need to install any additional libraries.
What is Lazy Loading?
When we launch a React web application, it usually bundles the entire application at once, loading everything including the entire web app pages, images, content, and much more for us, potentially resulting in a slow load time and overall poor performance, depending on the size of the content and the Internet bandwidth at the time.
Lazy loading allows us to load specific components only when they are needed. Typically, we also perform code splitting into logical components that can be lazy loaded with the content as well.
For example, if we have a dashboard page that displays a lot of information from various sources when clicked, it's always best to keep these components and pages lazy-loaded, so they only load when needed or required by the user.
Note: The process of splitting a large bundle of code into multiple bundles that can be loaded dynamically with the overall goal of avoiding performance issues associated with over-sized bundles is known as code-splitting. This is accomplished without reducing the amount of code in our app.
In summary, lazy loading allows us to render components or elements on demand, making our app more efficient and providing a better user experience.
Note: Single Page Applications (SPAs) are designed to contain all pages and content within a single document/page. That's why lazy loading comes in especially handy when developing SPAs.
How to Implement Lazy Loading in React
So far, we've seen what lazy loading is and why it's important to implement. Now, let's look at how we can implement it in our React applications, using two React features that make code-splitting and lazy loading easy to implement - React.lazy() and React.Suspense.
React.lazy()
is a function that allows us to render dynamic imports in the same way as regular components. Using dynamic imports alongside the React.lazy()
will enable us to import a component just before it renders on a screen. An important thing to note is that React.lazy()
accepts a function as an argument - that function must call the dynamic import()
in its body.
React.Suspense
enables us to specify the fallback prop which takes in a placeholder content that would be used as a loading indicator while all the lazy components get loaded.
Let's get started by looking at how we can implement lazy loading in our imported components, and then how we can implement it in our routes so that pages are not loaded until we navigate to them.
Getting Started
Suppose we have our React application, and we imported the About
component into the Home
:
import AboutUs from './About';
const Home = () => {
return (
<div className="App">
<h1>Home Page</h1>
<AboutUs />
</div>
);
};
export default Home;
We can now implement lazy loading by making use of React.lazy()
:
import React from 'react';
// Lazy loading
const AboutUs = React.lazy(() => import('./About'));
const Home = () => {
return (
<div className="App">
<h1>Home Page</h1>
<AboutUs />
</div>
);
};
export default Home;
Note: React.lazy()
used this way returns a Promise
object. That promise resolves to a module that contains a React component we want to lazy load in its default
export.
We've implemented lazy loading using React.lazy()
, but the code above will always throw an error saying that our “React component suspended while rendering, but no fallback UI was specified”
. This can be fixed by wrapping the component with React.Suspense
's fallbackz
and attaching the fallback props as we explained earlier:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
import React from 'react';
const AboutUs = React.lazy(() => import('./About'));
const Home = () => {
return (
<div className="App">
<h1>Home Page</h1>
<React.Suspense fallback={<div>Loading...</div>}>
<AboutUs />
</React.Suspense>
</div>
);
};
export default Home;
Note: The fallback prop can take a component to show before the original content loads up.
Additionally, we can decide to destructure the React import to make the code cleaner and more readable:
import { lazy, Suspense } from 'react';
const AboutUs = lazy(() => import('./About'));
const Home = () => {
return (
<div className="App">
<h1>Home Page</h1>
<Suspense fallback={<div>Loading...</div>}>
<AboutUs />
</Suspense>
</div>
);
};
export default Home;
So far, we've seen how to implement lazy loading in our imported components. Now, let's see how to implement it in our routes while routing with React Router.
How to Implement Lazy Loading With React Router
Lazy routing is actually a good practice for routes that have a lot of content and may slow down your application's load time. Implementing lazy loading for React routes is almost identical to what we did earlier when lazy loading dynamically imported components.
Lazy loading React routes refers to dynamically importing a component only when it's needed. For example, say we have two routes in our application and two components representing those routes. If we implement mentioned routing in the following way, each component will be loaded only when we navigate to the corresponding route:
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./Home'));
const Products = lazy(() => import('./Products'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
export default App;
Conclusion
In this guide, we learned what lazy loading and code splitting are, how to implement them, and that the best place to implement lazy loading is with routes. This avoids rendering the entire page at once, which may result in a slower load time when dealing with pages with large amounts of content.