Code Splitting for React

Adaeze UkaAdaeze Uka
16 min read

Prerequisite

  • Familiarity with React Router

  • Proficiency in using DevTools, preferably in Chrome.

  • Installation of the latest version of Node is required.

  • Understanding of React Suspense concept.

Introduction

When leveraging React to create your high-quality application, the anticipation is for enhanced speed compared to traditional JavaScript development. However, if your React app encounters sluggish performance, a straightforward and library-light approach to address this is through code splitting.

This article delves into the concept of code splitting, elucidating what it entails and guiding you on its implementation to optimize the efficiency of your applications. Explore the benefits of code splitting and learn how to seamlessly integrate it into your React projects for improved performance.

Goals

This article aims to:

  • Describe the process of code splitting.

  • Provide a clear understanding of what code splitting is in the context of React applications.

  • Offer practical insights into implementing code splitting in React applications.

  • Highlight the advantages and benefits of incorporating code splitting into React applications.

Outline

  • Understanding Code Splitting

    • Implementing Code Splitting
  • Benefits of code splitting

  • Part 1: Building the Initial Codebase

    • Setting Up

    • Building the Web App

  • Part 2: Identifying Bottlenecks

  • Part 3: Code Splitting with Lazy loading

Understanding Code Splitting

In the realm of software development, a cardinal objective is to craft applications that are not only well-optimized but also efficient and high-performing. Despite its relatively low profile in developer discussions, code splitting has garnered increased attention, particularly with the surge of web applications and the imperative to enhance user experiences.

At its core, code splitting entails the dissection of a software application into modularized components, leading to tangible benefits such as improved performance and expedited load times.

In a more succinct definition, code splitting is a technique that strategically dissects an application's codebase into smaller, more manageable parts. The primary goal is to load only the code essential for the current user's interaction, thereby mitigating the initial load time of the application.

In today's digital landscape, where users demand swift and responsive interfaces, understanding and implementing code splitting is imperative. Failing to do so could result in the loss of customers and, consequently, a negative impact on business.

When developers build applications, a common practice involves bundling all JavaScript code into sizable files. While this method offers simplicity and facilitates easy deployment, it can lead to substantial file sizes that, in turn, hinder the initial page load and overall web app interactivity.

Implementing Code Splitting

Code splitting can be implemented in various development environments and technologies.

Here are some common methods:

  • Webpack: If you're using Webpack as your build tool, you can easily implement code splitting using its dynamic import feature. Webpack will automatically split your code into smaller chunks and load them on-demand when needed.

  • React: React, a popular JavaScript library for building user interfaces, provides a built-in mechanism for code splitting using the React.lazy() and Suspense APIs. These allow you to load components asynchronously as users navigate your application, and will be our focus for today's article.

  • Vue.js: Vue.js, another popular JavaScript framework, supports code splitting through dynamic imports, which lets you load components on-demand, improving the performance of your Vue applications.

  • Server-Side Rendering (SSR): In server-side rendered applications, you can implement code splitting by dynamically importing modules to reduce the initial server response size and improve the time-to-interactive metric.

Benefits of Code Splitting

Some worthwhile benefits of code splitting includes:

  • Faster Initial Loading Time: Code splitting enables the loading of of only the essential code required for the initial page view, with this, users don't have to wait for the application to become fully usable.

  • Improved User Experience: Faster load times contribute to a better user experience, which leads to better satisfaction and engagement.

  • Lower Bandwith Consumption: Smaller code chunks mean the amount of data transferred over the network is lesser.This reduces bandwidth and potentially saves costs for both users and developers.

  • Efficient caching: Code splitting can improve browser caching as the only the changed or updated code chunks need to be loaded,reducing the need to download the entire application code each time.

  • Better Maintainability: Breaking the application into more modular parts can make the codebase more manageable and easier to maintain, test and debug.

To illustrate the concept of code splitting in React today, we'll build a demo web app featuring simple toggle and alert functions. This demo will unfold in three major stages. Firstly, we'll set up our React environment using ViteJS and code up the web app.

Afterward, we'll inspect the web app using a browser developer tool—preferably Chrome—to identify any inefficiencies. This critical analysis will help us understand the initial performance bottlenecks.

Following the inspection, we'll make significant changes to different components of the web app to implement code splitting. By optimizing the loading of components on-demand, we aim to observe tangible improvements in the app's overall efficiency.

Part 1: Building the Initial Codebase

Since this is a very practical topic, I felt it would be best to orient this article to actually make a litle demo project that mirrors a standard react codebase and then dissect the project while using codesplitting to make the project better in terms of efficiency.

Setting up

Let's set up the ViteJS environment.

  1. Before we begin, ensure that you have the latest version of Node installed on your computer. As of the time of writing this article, the latest version is 21.1.0. Verify your Node installation by running the following command in your terminal:
node --version
  1. Following the Node installation, let's set up the React working environment. Execute the following command in your terminal to install Vite globally:
npm install -g create-vite
  1. Switch to the directory where you intend to create the web app. Use the following command to navigate to your desired directory:
cd project-location
  • Replace project-location with the location you want to make the app.
  1. Once you're inside the desired location, run the following command to create the Vite-React project:
npm create vite@latest
  1. You'll be prompted to provide information about your project. Feel free to name it as you wish; for instance, I'll name mine split-app. Enter the relevant details when prompted.

  1. When prompted about the framework, choose "react," and then select the "JavaScript" option. This ensures the project is set up with React using JavaScript.

NOTE: If you encounter challenges while selecting options using Git Bash, it's recommended to use an Integrated Development Environment (IDE) terminal such as VSCode for a smoother experience.

  1. Execute all the commands displayed to install the necessary dependencies. This ensures that your project has all the required packages and dependencies installed.
cd split-app
npm install
npm run dev
  • Running the command npm run dev will initiate a visual output on a specific port, confirming that the Vite-React environment is ready for use. This visual output typically indicates that your development server is up and running.
  1. Double click on the port.

Here's the result

Making the Web App.

Now, let's start building the app. At this point, the essential packages should be installed.

  1. An important package we'll utilize is React Router, facilitating the behavior of our web-page as a Single Page Application (SPA). To install this package, use the following terminal command:
npm install react-router-dom@6
  1. Another crucial package will be required later for error handling. Use this command to install:
npm i react-error-boundary
  1. Once the packages are installed, delete the default files in the /src folder of your app and ensure it has the following file and folder arrangement:
/src 
  /components
    Admin.jsx
    AdminHello.jsx
    ErrorFallback.jsx
    Header.jsx
    Home.jsx
    Layout.jsx
  App.jsx
  index.css
  main.jsx

These components will do these things:

  • Admin.jsx: The main component for the admin section.

  • AdminHello.jsx: A component for greeting.

  • Header.jsx: A component for the header section.

  • ErrorFallback.jsx: Displays error messages.

  • App.jsx: The combined component for your React application.

  • index.jsx: Contains global styles for the entire application.

  • main.jsx: The main entry point where the React application is initialized and rendered into the HTML root element.

Next, let's populate all the files with code. To keep the article succinct, I won't explain every line of code but will provide a generalized idea of what each file's code will accomplish.

  1. Now, let's create the main components, starting with /components/Admin.jsx.
import AdminHello from "./AdminHello";

const Admin = () => {
  return (
    <main className="admin">
      <AdminHello />

      <h2>Author</h2>
      <p>
        Debitis culpa iste perspiciatis quos odit consequatur dicta nobis?
        Veritatis temporibus facere, quibusdam obcaecati molestiae possimus
        repellendus ratione? Rerum harum esse eius doloribus distinctio a neque
        sed dolore saepe voluptatem odit eligendi debitis quas molestiae ullam
        assumenda, fuga exercitationem quasi repudiandae excepturi. Ipsum, ex
        commodi. Odio minus adipisci voluptas assumenda possimus a magni
        necessitatibus libero. Quis alias dolorum dolor odio doloremque qui
        adipisci molestias eveniet eaque, consequuntur voluptatem numquam,
        aperiam beatae tenetur exercitationem cupiditate, necessitatibus nobis
        ea tempora ex. Tempore, nobis ut rem accusamus deleniti aperiam, vero
        dignissimos at, unde error sapiente eaque. At enim laudantium autem unde
        mollitia blanditiis?
      </p>
      <p>
        Reiciendis totam, repellendus maiores suscipit, iure unde in praesentium
        voluptatem nobis enim provident illum cumque at molestias, ratione animi
        qui possimus deserunt. Harum repudiandae excepturi beatae explicabo eius
        quis nemo eum sapiente temporibus a non debitis velit, optio distinctio
        consequuntur repellat cumque, ab possimus voluptate saepe perspiciatis
        quam. Quam aliquam, quaerat esse porro, tenetur, dolorem provident
        distinctio vero nostrum ab obcaecati? Amet voluptate animi tenetur
        praesentium ullam saepe iste a. Explicabo, distinctio temporibus!
        Laudantium quod reprehenderit dignissimos voluptates animi repudiandae,
        ad perferendis dolore a, rem voluptate repellat quasi assumenda magnam
        eaque. Sit dignissimos libero atque officiis doloribus deserunt. Eos,
        architecto!
      </p>
      <p>
        Tenetur, sed sequi officiis at dolor, suscipit similique esse molestiae
        ea animi consectetur aliquid facere itaque provident quasi. Adipisci
        ipsum eos odio harum necessitatibus quas corporis reprehenderit
        perspiciatis unde natus labore architecto libero saepe eaque, vero
        itaque distinctio exercitationem. Tempora qui sequi maxime dolorum
        perferendis neque minus quaerat molestias eius, libero incidunt pariatur
        similique officia ea? Eius sunt tenetur consequatur vitae, earum maxime
        assumenda dicta vero. Neque doloremque dignissimos saepe, deserunt
        officiis nam labore non rem vero, animi dolores quae magnam.
        Reprehenderit provident molestias eaque, suscipit sapiente dolores nemo
        excepturi iste nobis fugiat distinctio iure! Eius ad hic perferendis
        explicabo?
      </p>

      <p>
        Perspiciatis accusantium quo optio repudiandae dolorem repellat, est
        laborum adipisci quas ipsum ad explicabo dolorum iusto quos, similique
        numquam iure? Perspiciatis perferendis architecto, fugit, veritatis,
        voluptatum eos officiis autem distinctio delectus cupiditate et ea quas
        laborum necessitatibus fuga voluptate! Nihil, sapiente? Eaque libero
        exercitationem cumque porro, dolorum ab deserunt eius corporis dicta
        magni impedit incidunt nam, laudantium tenetur tempora explicabo
        consequuntur deleniti possimus accusantium ad non atque dolorem
        officiis. Voluptatibus cum neque alias eveniet? At asperiores molestias
        impedit illo nam, architecto magnam perspiciatis neque eaque fugiat
        tempora, sequi natus laboriosam totam, unde eum repellendus rerum odio.
        Sequi, asperiores commodi. Expedita.
      </p>
      <p>
        Esse ad voluptatibus libero officiis optio, beatae accusamus dolorem
        consequatur consectetur qui unde in assumenda! Eius, veritatis.
        Reiciendis hic sed placeat cumque dolor maxime eius esse molestias
        dolorem, sequi voluptatum, aliquam sapiente debitis. Hic labore dolorum
        deleniti quo soluta, laudantium aliquid sed! Totam laborum aspernatur
        nulla perferendis ut assumenda numquam. Maiores sunt consectetur
        deserunt dolorum dignissimos quidem cupiditate pariatur accusantium
        saepe, ipsa corporis voluptatibus perspiciatis, alias nostrum minus
        molestias suscipit esse? Voluptatem id quae magni ipsum aperiam iure,
        aliquam saepe maxime inventore adipisci aliquid perspiciatis ea quas?
        Dolorum id mollitia, aperiam quos magnam perferendis officia doloremque
        dolores, placeat ipsa ab?
      </p>
      <p>
        Expedita veniam, earum mollitia error iste adipisci nemo asperiores
        veritatis velit facilis amet provident accusantium commodi quasi nisi,
        fuga sed, dicta deserunt minima dolore eos corrupti! Vitae error,
        accusamus modi facilis omnis atque repellendus inventore id sed mollitia
        quidem corrupti quibusdam labore ea quia iste voluptate sit repudiandae
        quod aliquid doloremque eum nesciunt possimus eveniet! Nisi itaque ab
        quibusdam sunt fugiat praesentium ipsum, doloribus consequuntur harum
        neque? Debitis placeat, unde aspernatur laborum excepturi eum sapiente
        consequatur necessitatibus? Nulla sequi atque placeat unde illum
        accusamus ipsam, reiciendis tenetur delectus ducimus laborum cumque quia
        sit laboriosam quibusdam necessitatibus, voluptatem fuga id itaque!
      </p>
    </main>
  );
};
export default Admin;
  1. Next, let's work on /components/AdminHello.jsx.
const AdminHello = () => {
  return <h3>Hello, Welcome to Open Replay</h3>;
};
export default AdminHello;
  • This renders another page component.
  1. Up next/components/ErrorFallback.jsx.
const ErrorFallback = ({ error, resetErrorBoundary }) => {
  return (
    <div className="error">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
};
export default ErrorFallback;
  • This is the error fall back messsage.
  1. Next up, /components/Header.jsx.
const Header = () => {
  return (
    <header>
      <h1>Open Replay Blog</h1>
    </header>
  );
};
export default Header;
  • This is the header component.
  1. For the home page component, /components/Home.jsx.
import { Link } from "react-router-dom";

const Home = () => {
  return (
    <main className="home">
      <h2>Open Replay</h2>
      <p>
        <Link to="/admin">Read more on Open Replay</Link>
      </p>
      <p>
        <b>Chief Editor :</b> Federico Kereki
      </p>
    </main>
  );
};
export default Home;
  1. Certainly, let's add code to /components/Layout.jsx to ensure a proper layout.
import { Outlet } from "react-router-dom";
import Header from "./Header";

const Layout = () => {
  return (
    <div className="App">
      <Header />
      <Outlet />
    </div>
  );
};
export default Layout;
  1. Moving on to the main application files, let's start with/src/App.jsx.
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import Layout from "./components/Layout";
import Admin from "./components/Admin";

function App() {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />

        <Route path="admin" element={<Admin />} />
      </Route>
    </Routes>
  );
}

export default App;
  • This defines routes for the application.
  1. /src/index.css provides overall styling.
@import url("https://fonts.googleapis.com/css2?family=Poppins&display=swap");

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --ADMIN-BG: #fdf7e4;
  --HOME-BG: #faeed1;
  --HEADER-COLOR: #ded0b6;
  --HEADER-BG: #bbab8c;
}

body {
  min-height: 100vh;
  font-family: "Poppins", sans-serif;
}

#root,
main {
  min-height: 100vh;
}

main {
  display: grid;
  place-content: center;
  padding: 1rem;
}

header {
  background-color: var(--HEADER-BG);
  color: var(--HEADER-COLOR);
  position: sticky;
  top: 0;
  padding: 1rem;
  z-index: 1;
}

.home {
  background-color: var(--HOME-BG);
  text-align: center;
}

.admin {
  background-color: var(--ADMIN-BG);
}

.admin p {
  margin-bottom: 1rem;
}
  1. /src/main.jsx gets loaded into the HTML document.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path="/*" element={<App />} />
      </Routes>
    </BrowserRouter>
  </React.StrictMode>,
);
  • This contains the main react-router code.
  1. After making all these changes, run the following command in your terminal and click on the local host link provided.
npm run dev
  • This concludes the process, resulting in a simple homepage and a link that, when clicked, expands to reveal more content.

Part 2: Identifying Bottlenecks

In my perspective, a pragmatic software engineer doesn't categorize software as strictly right or wrong but rather on a spectrum of efficiency. While there's nothing inherently flawed with the demo as it functions perfectly, its efficiency might be optimized further.

The crux of our discussion in this article is that if the client doesn't require the full code at a specific time, it's not best practice to provide everything at once. It's crucial to deliver only the necessary code to the client at a given point, emphasizing the importance of code splitting for enhanced efficiency and a more streamlined application.

We want to address an issue with the split-app, our example React app. Currently, upon loading, all components, including /components/Admin.jsx and /components/AdminHello.jsx, are being loaded. However, these components are intended to be loaded only when an admin is using the app, not on the initial load.

Our objective with code splitting is to prevent the loading of the admin components until the "read more" link is clicked. To visualize these inefficiencies, let's turn to DevTools:

  1. Open chrome developer tools by either right-clicking on the demo and choosing "inspect" or option or pressing Option + ⌘ + J (on macOS), or Shift + CTRL + J (on Windows/Linux).

  1. Click on the "Sources" option, and right-click for a moment on the refresh option. Then, select "Empty Cache and Hard Reload." This ensures a fresh start, clearing any cached data that might affect the loading behavior of the application.

  1. After refreshing the page, navigate to the src folder and then to the components folder. Observe the files, and you'll notice that both Admin.jsx and AdminHello.jsx are present. As you are aware, these files should only be loaded on demand, highlighting the current inefficiency we aim to address with code splitting.

Part 3: Code Splitting with Lazy Loading

Now, let's optimize our application. Keep in mind that the objective of this demo is to ensure that the files /src/components/Admin.jsx and /src/components/AdminHello.jsx are loaded only when the "read more" link is clicked. This is the essence of code splitting—to load specific components on-demand for a more efficient application. Turn over to /src/App.jsx because that is the file where we would be splitting the application. And make changes to the file like this:

import { Routes, Route, useNavigate } from "react-router-dom";
import Home from "./components/Home";
import Layout from "./components/Layout";
// import Admin from "./components/Admin"
import { ErrorBoundary } from "react-error-boundary";
import ErrorFallback from "./components/ErrorFallback";
import { lazy, Suspense } from "react";

const Admin = lazy(() => import("./components/Admin"));

function App() {
  const navigate = useNavigate();

  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />

        <Route
          path="admin"
          element={
            <ErrorBoundary
              FallbackComponent={ErrorFallback}
              onReset={() => navigate("/")}
            >
              <Suspense fallback>
                <Admin />
              </Suspense>
            </ErrorBoundary>
          }
        />
      </Route>
    </Routes>
  );
}

export default App;

In the previous version of this file, <Route path="admin" element={<Admin />}/> is responsible for handling and granting access to the admin page.

To ensure that the admin pages are loaded in the browser only when the admin link is clicked, here are the changes made in src/App.jsx:

  • import { Routes, Route, useNavigate } from "react-router-dom": This line imports specific components and functions from the react-router-dom library for setting up routing in the application.

  • The newly added import statements import { ErrorBoundary } from "react-error-boundary" and import ErrorFallback from "./components/ErrorFallback" are part of React's system for catching errors anywhere in the component tree and logging those errors.

  • Meanwhile, import { lazy, Suspense } from "react" are modules used for lazy loading and code splitting. Lazy loading defers the loading of certain assets until they are needed.

  • This line const Admin = lazy(() => import('./components/Admin')) uses the lazy module from React to lazy load the Admin component.

  • This component <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => navigate('/')}> is the standard way of using the ErrorBoundary and ErrorFallback modules to handle errors. The navigate function redirects to the home page in case of errors.

  • <Suspense fallback> uses the Suspense module from React to enable lazy loading. The fallback prop is required and represents the UI to show while the main UI components are being lazy loaded. However, since it isn't in the scope of this article, I chose to not create a proper fallback UI.

To confirm if our changes worked:

  1. Exit your current thread on your terminal using CTRL+C. Save your changes.

  2. Restart the server with this command, and click on the port:

npm run dev
  1. Open developer tools, go to the "Sources" option, and refresh with the "Empty Cache and Hard Reload" option.

  1. Toggle the src folder in DevTools, and observe that the admin files /components/Admin.jsx and /components/AdminHello.jsx are absent. This behavior occurs due to lazy loading.

  1. Now, if you click the link on the homepage, /components/Admin.jsx and /components/AdminHello.jsx will be loaded in the browser.

Imagine if this were a React codebase with millions of lines of code. Consider the time it would take to load all that code at once to the client and the impact on user experience.

This my friend, is the magic of code splitting. With code splitting, you can selectively deliver code to users based on their interaction with your web application, optimizing performance and providing a more responsive user experience.

Conclusion

This article has unraveled a powerful technique for enhancing the efficiency of React applications.

We embarked on a comprehensive exploration, aiming to provide a clear understanding of what code splitting entails in the context of React development.

The goals included describing the process of code splitting, highlighting its advantages,and offering practical insights into its implementation.

By breaking down the complexities of code splitting, we've illustrated its potential to transform React applications, especially in scenarios where loading large volumes of code upfront could hinder user experience.

The practical demo, focusing on a web app example, allowed us to witness firsthand how code splitting can be a game-changer. Through strategic implementation, we ensured that components are loaded only when necessary, optimizing performance.

Code splitting isn't just a technique; it's a solution to the challenge of delivering large-scale applications without sacrificing speed. As we conclude this exploration, consider incorporating code splitting into your React projects, unlocking the magic of efficient and responsive user experiences.

Resources

10
Subscribe to my newsletter

Read articles from Adaeze Uka directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Adaeze Uka
Adaeze Uka

A passionate beautiful and curious beaut from Africa, I can't wait to see what the world of programming has to offer