Component Splitting
Component Splitting
Component Splitting (also known as Code Splitting at the component level) is a technique used to improve the initial load time of a React application.
Instead of forcing the user’s browser to download the JavaScript for every single component in your entire app before they can see the homepage, you “split” your code into smaller chunks. The browser only downloads the chunks it needs for the current page, and requests other chunks on-demand when the user navigates.
Why split components?
Imagine a heavy dashboard application. It has a Home view, a Settings view, and a DataExport view. The DataExport view relies on a massive charting library (like Highcharts or Recharts) that adds 500kb to your bundle size.
If you import all three components normally at the top of your App.js:
import Home from './Home';
import Settings from './Settings';
import DataExport from './DataExport'; // Includes 500kb charting library!
Every user who visits the Home page has to download that 500kb charting library, even if they never click on the DataExport tab. This severely degrades performance.
How to split components
React provides two built-in APIs to handle component splitting: React.lazy and Suspense.
1. React.lazy()
React.lazy lets you render a dynamic import as a regular component.
Instead of standard static imports, you use lazy() combined with a dynamic import() statement. Webpack (or Vite) sees this dynamic import and automatically creates a separate physical file (a “chunk”) for that component during the build process.
// Static import (bundled together)
// import DataExport from './DataExport';
// Dynamic import (code split into a separate chunk)
import { lazy } from 'react';
const DataExport = lazy(() => import('./DataExport'));
2. Suspense
Because DataExport is now in a separate file, it takes time to download over the network when the user navigates to it. React needs to know what to show the user while it’s waiting for that file to download.
You use the <Suspense> component to provide a fallback UI (like a loading spinner) while the lazy component loads.
import { lazy, Suspense, useState } from 'react';
import Home from './Home';
const DataExport = lazy(() => import('./DataExport'));
function App() {
const [view, setView] = useState('home');
return (
<div>
<nav>
<button onClick={() => setView('home')}>Home</button>
<button onClick={() => setView('export')}>Export Data</button>
</nav>
<main>
{view === 'home' && <Home />}
{view === 'export' && (
// Suspense catches the "loading" state of the lazy component
<Suspense fallback={<div>Loading Export Tool...</div>}>
<DataExport />
</Suspense>
)}
</main>
</div>
);
}
Route-Based Splitting
The most common and effective place to introduce component splitting is at the route level. By making each page/route in your application a lazy component, you ensure users only download the code for the specific page they are visiting. Modern frameworks like Next.js handle this route-based code splitting automatically.
Interview Questions
Q: How can physically splitting a large component into smaller ones improve performance?
A: If a highly localized, rapid state change (like tracking a mouse hover or typing in an input) happens inside a massive component, the entire component re-renders. By isolating that state into a tiny child component, only the child re-renders on state change, sparing the heavy parent component.