React Suspense
Declaratively handle loading states for async rendering
Overview
Suspense lets a component "wait" for something before rendering, showing a fallback in the meantime. React catches the pending state thrown by a suspending child and renders the nearest <Suspense> boundary's fallback until the data or code is ready. It powers lazy code-splitting, streaming SSR, and data fetching in modern frameworks.
Syntax / Usage
Wrap suspending children in a boundary and provide a fallback.
import { Suspense, lazy } from 'react'
const Analytics = lazy(() => import('./Analytics'))
function Dashboard() {
return (
<Suspense fallback={<p>Loading analytics…</p>}>
<Analytics />
</Suspense>
)
}
Examples
Data fetching with the use hook in a client component—use suspends until the promise resolves:
import { use, Suspense } from 'react'
function Profile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise)
return <h1>{user.name}</h1>
}
function Page({ userPromise }: { userPromise: Promise<User> }) {
return (
<Suspense fallback={<Skeleton />}>
<Profile userPromise={userPromise} />
</Suspense>
)
}
Nest boundaries so independent regions stream in separately and pair with an error boundary to catch rejected promises:
<ErrorBoundary fallback={<Error />}>
<Suspense fallback={<Skeleton />}>
<Feed />
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
</Suspense>
</ErrorBoundary>
Common Mistakes
- Creating the promise inside the suspending component, so it re-fetches on every attempt
- Forgetting an error boundary—rejected promises otherwise bubble uncaught
- Expecting
useEffect-based fetching to trigger Suspense; only suspense-enabled sources do - Wrapping too coarsely, so one slow region blocks unrelated content from showing
- Using
useconditionally—it follows the Rules of Hooks like other hooks
See Also
react-error-boundaries react-server-components use-effect