Code splitting is a performance optimization technique that allows you to split your application into smaller bundles that can be loaded on demand, rather than loading the entire application at once.
Why Use Code Splitting?
- Reduces initial bundle size
- Improves initial load time
- Loads only the code needed for the current view
- Better user experience, especially on slow networks
Methods for Code Splitting in React
1. Dynamic import()
Syntax
The foundation of code splitting in modern React applications:
// Regular import (not split)
import { add } from './math';
// Dynamic import (code split)
import("./math").then(math => {
console.log(math.add(16, 26));
});
2. React.lazy
for Component-level Splitting
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
3. Route-based Splitting
A common pattern with React Router:
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const Contact = lazy(() => import('./routes/Contact'));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading page...</div>}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
</Suspense>
</Router>
);
}
4. Library/Library Parts Splitting
Split large libraries that aren’t needed immediately:
const Plot = React.lazy(() => import('react-plotly.js'));
function ChartComponent() {
return (
<Suspense fallback={<div>Loading chart...</div>}>
<Plot
data={[/* ... */]}
layout={/* ... */}
/>
</Suspense>
);
}
5. Named Exports with Lazy Loading
For modules with named exports:
// components.js
export const ComponentA = () => <div>A</div>;
export const ComponentB = () => <div>B</div>;
// Usage
const ComponentA = lazy(() =>
import('./components').then(module => ({ default: module.ComponentA }))
);
const ComponentB = lazy(() =>
import('./components').then(module => ({ default: module.ComponentB }))
);
Error Boundaries
Always wrap lazy-loaded components with error boundaries to handle loading failures:
import React, { Suspense } from 'react';
import ErrorBoundary from './ErrorBoundary';
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function MyComponent() {
return (
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</ErrorBoundary>
);
}
Webpack Magic Comments
When using Webpack, you can add magic comments to provide more control:
const Component = lazy(() =>
import(/* webpackChunkName: "component-chunk" */ './Component')
);
const OtherComponent = lazy(() =>
import(/* webpackPrefetch: true */ './OtherComponent')
);
Preloading Strategies
- Prefetch: Load during browser idle time
- Preload: Load with higher priority
- Webpack magic comments:
/* webpackPrefetch: true */
or/* webpackPreload: true */
const ImportantComponent = lazy(() =>
import(/* webpackPreload: true */ './ImportantComponent')
);
const MaybeLaterComponent = lazy(() =>
import(/* webpackPrefetch: true */ './MaybeLaterComponent')
);
When to Use Code Splitting
- Large components that aren’t needed immediately
- Routes in single-page applications
- Heavy third-party libraries
- Components behind user interactions (modals, tabs, etc.)
Code splitting is a powerful tool in your React performance optimization toolkit, but use it judiciously – over-splitting can lead to too many network requests.