Code Splitting with lazy & Suspense
Load parts of your app only when they are needed, so the first page opens faster.
What you will learn
- Explain why a smaller initial bundle matters
- Split a component out with React.lazy
- Show a fallback while it loads with <Suspense>
Why split the bundle?
When you build a React app, all your code is packed into one big file called the bundle (think of it as one giant JavaScript download). The bigger the bundle, the longer the user waits before the first screen appears. Code splitting breaks that one file into smaller chunks and loads each chunk only when it is actually needed — so a page the user never visits is never downloaded.
A concrete example: imagine a dashboard with a heavy Reports page full of charts. Most users open the Home page and never click Reports. With code splitting, the charts code only downloads the moment someone opens Reports — everyone else gets a faster start.
lazy + Suspense
Two pieces work together. React.lazy wraps an import so the component loads on demand. <Suspense> wraps the lazy component and shows a fallback (a loading message or spinner) during the brief wait while that chunk downloads. This belongs in a real project, so it is shown as code with its Output.
import { lazy, Suspense } from 'react';
// Instead of: import Reports from './Reports.jsx'
const Reports = lazy(() => import('./Reports.jsx'));
function App() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading reports…</p>}>
<Reports />
</Suspense>
</div>
);
}Reading it line by line:
- A normal
import Reports from './Reports.jsx'would bundle Reports into the main file. Instead we writeconst Reports = lazy(() => import('./Reports.jsx')). The() => import(...)is a dynamic import — it tells the build tool “put Reports in its own chunk and fetch it only when first used”. - Because the chunk takes a moment to download, we wrap
<Reports />in<Suspense fallback={<p>Loading reports…</p>}>. Thefallbackis what shows during that download. - On first render, React asks for the Reports chunk; while it arrives the user sees Loading reports…; once it lands, React swaps in the real Reports page.
Note: Output: The Dashboard heading appears instantly. Where Reports will go, you briefly see Loading reports…, then the full Reports page once its chunk has downloaded. On later visits the chunk is cached, so there is no wait.
The most common use: per-route splitting
The biggest win is splitting by route — each page becomes its own chunk, so opening the site downloads only the Home page, not every page at once. It pairs perfectly with React Router:
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home.jsx'));
const About = lazy(() => import('./pages/About.jsx'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<p>Loading…</p>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}Each page is wrapped in lazy(() => import(...)), so the build tool produces a separate chunk per page. One <Suspense> around the <Routes> shows Loading… during the brief moment a new page’s chunk downloads. The user’s first visit downloads only the Home chunk; the About chunk arrives the first time they click About.
Tip: Combine this with an error boundary: wrap the <Suspense> in an <ErrorBoundary> so that if a chunk fails to download (flaky network), the user sees a friendly retry message instead of a crash.
Watch out: Do not lazy-load everything — each chunk is a tiny extra network request. Split the big, rarely-visited parts (heavy pages, large charts, an admin area). Small components that show immediately are fine to keep in the main bundle.
Q. What is the job of <Suspense fallback={...}> around a lazy component?
✍️ Practice
- Take a multi-page router app and convert each route to a
lazyimport wrapped in one<Suspense>. - Add a custom spinner component as the
fallbackinstead of plain text.
🏠 Homework
- Lazy-load one heavy section of an earlier app and note (in the browser Network tab) that its chunk only downloads when that section first appears.