Problem

React is a library for building single-page web applications. Therefore by default, it exports a single chunk file with all the pages and their depending libraries resulting in huge single bundle size. Even if the user is only visiting a page with small data in it, the whole package needs to be downloaded as a whole single chunk file. This is not acceptable for a good user experience on our website.

Introducing "React.Lazy". A function that lets you split and load different sections of our React application as requested by the user.

Example Scenario

Let's see how a default react application with two pages is implemented:

import React from "react";
import { BrowserRouter as Router, Route, Link, Routes } from "react-router-dom";
import Home from "./Home";
import LargePage from "./LargePage";
export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/largepage">LargePage</Link>
            </li>
          </ul>
        </nav>
        <Routes>
          <Route exact path="/" element={<Home />} />
          <Route path="/largepage" element={<LargePage />} />
        </Routes>
      </div>
    </Router>
  );
}
App.js
export default function Home() {
  return (
    <div>
      <h1>Home</h1>
    </div>
  );
}
Home.js
import moment from "moment";

export default function LargePage() {
  return (
    <div>
      <h1>Large Page</h1>
      <p>{moment.now()}</p>
    </div>
  );
}
LargePage.js
In the above example, we are using "react-router-dom" library (version - 6.3.0) for routing and a heavy "moment.js" package inside the "largePage.js" page.

In this case, the component "LargePage" containing the heavy "moment.js" package will be bundled and sent to every user irrespective of whether they are visiting the "Home" page or the  "Largepage". This is a waste of network and memory space.

Solution

We will introduce code splitting and lazy loading in order to avoid unnecessary large bundle sizes and loading time. The above "App.js" file can be refactored into the following code:

import React from "react";
import { BrowserRouter as Router, Route, Link, Routes } from "react-router-dom";
import Home from "./Home";
// import LargePage from "./LargePage";
import { lazy, Suspense } from "react"; //imported lazy and Suspense
const LargePage = lazy(() => import("./LargePage")); //lazy import "LargePage"

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/largepage">LargePage</Link>
            </li>
          </ul>
        </nav>
        {/* Added Suspense for loading state*/}
        <Suspense fallback={<h1>Loading...</h1>}>
          <Routes>
            <Route exact path="/" element={<Home />} />
            <Route path="/largepage" element={<LargePage />} />
          </Routes>
        </Suspense>
      </div>
    </Router>
  );
}
App.js

Here, we have added "lazy" and "Suspense" functions from the "react" library.

Lazy function

The "lazy" function enables the component to be only loaded when the user requests it.  Using "lazy" will split the code into separate chunk files, thereby avoiding the huge single bundle size that was delivered to the user in our earlier case. Lazy components will only be sent to the user upon loading a page that uses those components. Once loaded, these components will persist and won't need to be loaded again.

The syntax for "lazy" function is as follows:

import { lazy } from "react"; 
const LargePage = lazy(() => import("./LargePage")); 
The components that are lazily loaded need to be a default export.

Suspense function

The "Suspense" function is for providing a fallback for the lazy-loaded components such as when the component is being loaded. We would need to wrap out lazy-loaded components with the "Suspense" boundary like in our example and pass in a fallback for the loading state.

The syntax for the "Suspense" functions is as follows:

<Suspense fallback={<h1>Loading...</h1>}>
     <LazyLoadedComponents> //Wrap every component inside "Suspense".
</Suspense>

Conclusion

It is good practice to use lazy loading on pages with heavy libraries when building big projects. This way, we can reduce the bundle size and improve the performance of our entire application. Users shouldn't need to wait an extensive amount of time to just visit the home page.

Thank you for reading. Check out the featured articles in my blog to see more tutorials in React and several other technologies. See you in the next one.