How to Optimize React Apps with Code Splitting

Rahul DasuRahul Dasu
3 min read

Is your React app loading slowly? One of the simplest ways to speed it up is through code splitting. In this guide, we’ll explore what code splitting is, when and how to use it, and potential pitfalls you might encounter. This is your complete guide to mastering code splitting in React.

Introduction

Code splitting is the process of splitting your application into smaller chunks and loading them on demand. Instead of loading the entire application at once, you load only the parts you need. This reduces the initial load time and can significantly improve the performance of your app, especially as it grows in size.

Example Setup

In our example, we have a simple React application with a router set up using react-router-dom. We have three routes: Home, Store, and About. Each route loads a different component, and the Home component has some additional functionality.

Here’s a simplified structure of our application:

  • Home Page

  • Store Page

  • About Page

Implementing Code Splitting

Splitting Functions

First, let’s look at how to split out a simple function. For instance, we have a sum function that adds two numbers. Instead of loading this function with the initial bundle, we can load it only when needed.

// Import dynamically within the function
const handleSum = () => {
  import('./sum.js').then(module => {
    alert(module.sum(2, 2));
  });
};

// Trigger the function on button click
<button onClick={handleSum}>Sum</button>

Splitting Components

More commonly, you’ll split components. React provides a lazy function for this purpose, and Suspense to handle the loading state.

import React, { lazy, Suspense } from 'react';

const Home = lazy(() => import('./Home'));
const Store = lazy(() => import('./Store'));
const About = lazy(() => import('./About'));

<Suspense fallback={<div>Loading...</div>}>
  <Route path="/home" component={Home} />
  <Route path="/store" component={Store} />
  <Route path="/about" component={About} />
</Suspense>

When you navigate to a route, only the corresponding component is loaded, reducing the initial load time.

Handling Named Exports

If your components use named exports, you'll need to handle them differently in dynamic imports. For example, if About is a named export:

const About = lazy(() => import('./About').then(module => ({ default: module.About })));

Using Suspense

When lazy loading components, you should wrap them in a Suspense component to handle the loading state:

<Suspense fallback={<h1>Loading...</h1>}>
  <Switch>
    <Route path="/home" component={Home} />
    <Route path="/store" component={Store} />
    <Route path="/about" component={About} />
  </Switch>
</Suspense>

This will display "Loading..." while the component is being fetched.

Example with Artificial Delay

To simulate a slow network, you can use a delay function:

const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));

const Home = lazy(() =>
  wait(1000).then(() => import('./Home'))
);

This will show the loading state for one second before displaying the Home component.

Optimizing Further

If you have UI elements that should only be shown to certain users, such as an admin dashboard, you can conditionally load these components:

const AdminData = lazy(() => import('./AdminData'));

{isAdmin && (
  <Suspense fallback={<h2>Loading admin data...</h2>}>
    <AdminData />
  </Suspense>
)}

Conclusion

Code splitting can greatly enhance the performance of your React applications by reducing the initial load time and loading components on demand. By using React.lazy and Suspense, you can easily implement code splitting in your projects.

Thank you for reading, and happy coding! If you have any questions or tips about code splitting, feel free to share them in the comments below.

1
Subscribe to my newsletter

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

Written by

Rahul Dasu
Rahul Dasu

Software Engineer