How to Update Code Splitting in React
Code splitting is a powerful performance optimization technique that
reduces a React application’s initial bundle size by loading JavaScript
chunks only when they are needed. This article provides a direct,
step-by-step guide on how to update and implement modern code splitting
in your React applications using dynamic imports,
React.lazy, and Suspense.
1. Transition to Dynamic Imports
The foundation of code splitting in React is the dynamic
import() syntax. Unlike static imports that load at the top
of a file, dynamic imports are asynchronous and load modules on
demand.
To update a static import to a dynamic one, change this:
import HeavyComponent from './HeavyComponent';To this:
const HeavyComponent = import('./HeavyComponent');2. Implement React.lazy
To render a dynamically imported component as a regular React
component, you must wrap it in React.lazy. This function
takes a function that calls a dynamic import() and returns
a Promise which resolves to a module with a default export
containing a React component.
Update your component declaration like this:
import React, { lazy } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));3. Wrap with Suspense
Lazy components must be rendered inside a Suspense
component. Suspense allows you to show a fallback UI (like
a loading spinner or text) while the lazy-loaded component is being
fetched from the server.
import React, { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading component...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}4. Update Route-Based Code Splitting
The most common place to update code splitting is at the routing level. By splitting your application by route, users only download the code required for the page they are currently viewing.
Here is how to update your routes using
react-router-dom:
import React, { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading page...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
</Router>
);
}5. Handle Chunk Loading Failures
When you update your application and deploy a new version, old code chunks on the server may be deleted. If a user has an older version of your app open in their browser and tries to navigate to a lazy-loaded route, the chunk request will fail.
To resolve this, wrap your lazy components or routes in an Error Boundary to gracefully handle loading failures and prompt the user to refresh the page.
import React from 'react';
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Log the error to an analytics service
console.error("Chunk load failed: ", error);
}
render() {
if (this.state.hasError) {
return (
<div>
<h2>Something went wrong.</h2>
<button onClick={() => window.location.reload()}>Refresh Page</button>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;Integrate the Error Boundary around your Suspense
component:
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</ErrorBoundary>