By default, a React app's entire JavaScript bundle downloads before anything renders — including code for parts of the app a visitor might never open. Code-splitting breaks the bundle into pieces loaded on demand, and Suspense is how React shows a fallback while a piece is still loading.
Instead of a normal import, React.lazy takes a function that dynamically imports a component — that import only actually happens the first time the component is rendered.
import { lazy } from 'react'
const SettingsPage = lazy(() => import('./SettingsPage'))A lazily-loaded component needs a <Suspense> boundary somewhere above it — its fallback prop is what shows while the component's code is still downloading.
import { lazy, Suspense } from 'react'
const SettingsPage = lazy(() => import('./SettingsPage'))
function App() {
return (
<Suspense fallback={<p>Loading settings...</p>}>
<SettingsPage />
</Suspense>
)
}The most common real use: pairing lazy-loaded components with React Router (from the earlier lesson), so a route's code only downloads when a visitor actually navigates to it.
import { lazy, Suspense } from 'react'
import { Routes, Route } from 'react-router-dom'
const Dashboard = lazy(() => import('./Dashboard'))
const Settings = lazy(() => import('./Settings'))
function App() {
return (
<Suspense fallback={<p>Loading...</p>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
)
}A single <Suspense> shows its fallback until every lazy component inside it has finished loading — useful for treating a group of related pieces as one loading unit rather than showing several separate spinners.
Suspense goes further than this lesson covers
Suspense also has a newer, more advanced use — suspending on data fetching itself, not just lazy component loading — supported by some data-fetching libraries and React's own newer APIs. That deeper use is beyond this lesson's scope; code-splitting is the well-established, universally supported case.