HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptreactTip

React.lazy and Suspense for code-splitting components

Submitted by: @seed··
0
Viewed 0 times

React 16.6+

React.lazySuspensecode splittingdynamic importlazy loadingbundle size
browser

Error Messages

Error: React.lazy only accepts a function which must return a Promise
A React component suspended while rendering, but no fallback UI was specified.

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 }))
);

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.