Code-Split a Component to Shrink the Initial Bundle
React.lazy
`React.lazy` lets a component load on demand. Pair with `<Suspense>` for the loading UI.
What you'll learn
- Lazy-load a component
- Combine with `<Suspense>`
- Pick what to split
By default, your whole React app ships in one JavaScript bundle.
Big bundle → slow first paint. React.lazy lets you split off
parts that aren’t needed up-front.
The API
import { lazy, Suspense } from "react";
const Settings = lazy(() => import("./Settings.jsx"));
function App() {
return (
<Suspense fallback={<Spinner />}>
<Settings />
</Suspense>
);
} The import("...") is a dynamic import — the bundler (Vite,
webpack) turns it into a separate chunk that downloads only when
the lazy component first renders.
What To Split
Good candidates:
- Routes — settings page, admin page, infrequently visited pages
- Modals — the modal’s code only loads when the user opens it
- Heavy third-party widgets — chart libraries, rich text editors
- Anything below the fold or behind a click
Bad candidates:
- Tiny components — the request overhead is larger than the payload
- Stuff that’s always visible on first load
Per-Route Split
Common with React Router:
const Home = lazy(() => import("./Home.jsx"));
const Settings = lazy(() => import("./Settings.jsx"));
function App() {
return (
<Suspense fallback={<Spinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
} Visiting /settings for the first time triggers a chunk download
for that route only. Everything else loads on demand.
Default vs Named Exports
React.lazy only supports default exports out of the box. For
named exports, re-wrap:
// somewhere
const Settings = lazy(() => import("./Settings.jsx").then(m => ({ default: m.Settings }))); Up Next
Forwarding refs through your component.
forwardRef →