How to Optimize React Apps with Code Splitting
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.
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