React Lazy Load Component and Route with React Suspense
React Lazy Load Component and Route with React Suspense

In this post you will learn about lazy load of components inside React just by using React.lazy and Suspense. And additionally we will implement here lazy loading for routing because this is the best variant how to split your application and make it faster.

I already generated create-react-app for us. Here I has an App component, Home and About. They are all empty components with some text.

// App.js
const App = () => {
  return (
    <div className="container">
    </div>
  );
};
// Home.js
const Home = () => {
  return <h1>Homepage</h1>;
};

export default Home;
// About.js
const About = () => {
  return (
    <div>
      <h1>About</h1>
      <div>
        ...
        I pasted lots of text here.
        ...
      </div>
    </div>
  );
};

export default About;

The only important thing is that in About component I pasted a lot of text from lorem ipsum. Now this component is 10 kb of text. It will help us to understand why it is important to split our application.

Now let's try to render About component inside our application.

import App from './app'
const App = () => {
  return (
    <div className="container">
      <App/>
    </div>
  );
};

As you can see in browser our component is successfully rendered. But we are interested in our network tab.

Main chunk

As you can see our main chunk is 11 kb size.

The main point is that if we remove the import of About component it will be just 2 kb of data. It is not 11 kb like it was previously.

It happens because after importing component it is bundled together by webpack inside React. And we get all our components inside main chunk.

This is completely fine for small or medium applications. But at some point you build big production application and then it just becomes slow. Why is that? Browser must load your huge Javascript file, which can be 10 or 15 mb or data and this is quite a lot. After this browser must parse the whole file or Javascript and execute it. It takes a lot of time.

Lazy loading

This is why at the point it makes a lot of sense to load our components with lazy.

Lazy means that they are not available at the beginning but we load them later at some point.

So instead of just importing About component let's write this.

const About = React.lazy(() => import("./About"));

Here we use React.lazy function where we provide anonymous function inside and we return our import.

When we reload the page we see an error.

Suspense error

What does it means at all? We have a component which was delayed (suspended). It means that this component is not available for React at the beginning. React can't render this About component. And React doesn't know that we render instead.

We must render a fallback until we can render our component.

In order to do that we must wrap our component with React.Suspense.

<React.Suspense fallback={<div>Loading..</div>}>
  <About />
</React.Suspense>

Inside we must provide a fallback. What is fallback? It is either a component or just a plain markup. With this code we say that About component is lazy loaded and here is markup that we will render until it is available.

Now we don't have an error in browser. Now the question is if we have any difference from plain import.

Splitted chunk

Inside network now we have 2 chunks. Our main.chunk.js is just 3 kb and 1.chunk.js is 10 kb. Our About component was not bundled together with App component. We are getting this component as an additional script later when we load it.

Also if we will reload the page you can see a short blink until the component is rendered. You can see that our markup Loading... is rendered there.

The benefit of this might be not obvious because in both cases we see the rendered component and it's content really fast. But if you have some component which is huge and loads really long it makes a lot of sense to load it with lazy.

Lazy for routing

So this is how you use lazy loading inside React but it is not the best use case. This is not how you will write code in every single application. But what you would use in every application is lazy loading of routes.

The idea is to load router component and all it's children only when we just to the route.

It doesn't make a lot of sense on the homepage to load other components for other routes. In a huge application you can have like 200 or 500 different routes with lots of components inside. We don't want to bundle them together. We want to split all our components per route. This is exactly the use case of React.lazy for every single project.

Let's add link to 2 different pages and render our Home page.

const App = () => {
  return (
    <div className="container">
      <div>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </div>
      <Routes>
        <Route index element={<Home />} />
      </Routes>
    </div>
  );
};

There is no lazy loading here because any person who jumps to our website will visit homepage. It doesn't make a lot of sense to bundle it separately.

But now we want to load about page with lazy loading.

<Route
  path="about"
  element={
    <React.Suspense fallback={<div>Loading..</div>}>
      <About />
    </React.Suspense>
  }
/>

As you can see we just pass inside element property the same markup like we wrote previously. It's a React.Suspense with About component inside.

Let's check if it is working. When we load home page you can see in network that our bundle is just 2 kb of data. After we jump to about page it loads an additional chunk of 11kb and renders About component.

Obviously inside this component we just have text but in real application you can have big components with lots of logic and 1 mb per page can often happen. And actually if you are interested to learn how to implement select inside React for your next project make sure to check this post also.

📚 Source code of what we've done