patternjavascriptreactTip
React.lazy and Suspense for code-splitting components
Viewed 0 times
React 16.6+
React.lazySuspensecode splittingdynamic importlazy loadingbundle size
browser
Error Messages
Problem
All components are bundled into one JavaScript file by default. Large pages load all code upfront even if 80% is never needed on the initial view, increasing Time to Interactive and wasting bandwidth.
Solution
Use React.lazy with Suspense to split component code into separate chunks:
import { lazy, Suspense } from 'react';
// The import runs only when ChartPage first renders
const ChartPage = lazy(() => import('./ChartPage'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Router>
<Route path="/charts" element={<ChartPage />} />
<Route path="/settings" element={<Settings />} />
</Router>
</Suspense>
);
}
// Preload on hover to reduce perceived latency
const preloadSettings = () => import('./Settings');
<Link
to="/settings"
onMouseEnter={preloadSettings}
>Settings</Link>
// Named exports require a wrapper
const { SpecificComponent } = lazy(
() => import('./Components').then(m => ({ default: m.SpecificComponent }))
);
import { lazy, Suspense } from 'react';
// The import runs only when ChartPage first renders
const ChartPage = lazy(() => import('./ChartPage'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Router>
<Route path="/charts" element={<ChartPage />} />
<Route path="/settings" element={<Settings />} />
</Router>
</Suspense>
);
}
// Preload on hover to reduce perceived latency
const preloadSettings = () => import('./Settings');
<Link
to="/settings"
onMouseEnter={preloadSettings}
>Settings</Link>
// Named exports require a wrapper
const { SpecificComponent } = lazy(
() => import('./Components').then(m => ({ default: m.SpecificComponent }))
);
Why
Dynamic import() creates a split point in the bundle. The chunk is downloaded only when React.lazy's render is triggered. Suspense shows the fallback while the chunk loads, preventing the rest of the app from blocking.
Gotchas
- React.lazy only works with default exports — named exports need a .then() wrapper
- The Suspense boundary must be an ancestor — not a sibling — of the lazy component
- In React Server Components (Next.js App Router), use dynamic() from next/dynamic instead
- Lazy loading across route boundaries is the highest-value split — component-level splits are usually not worth it
Code Snippets
Route-level lazy loading
// Route-level code splitting
const Dashboard = lazy(() => import('./pages/Dashboard'));
<Suspense fallback={<PageSkeleton />}>
<Dashboard />
</Suspense>Context
When optimizing initial load time by splitting large page components or rarely-used features
Revisions (0)
No revisions yet.