React Suspense
Suspense lets components wait for something (data, code) and show a fallback in the meantime.
Basic example with lazy loading
tsx
const HeavyChart = lazy(() => import('./HeavyChart'));
function Dashboard() {
return (
<Suspense fallback={<Spinner />}>
<HeavyChart />
</Suspense>
);
}How it works with data fetching
Suspense only works with Suspense-enabled data sources: frameworks (Next.js Server Components), libraries (useSuspenseQuery in TanStack Query, Relay) or the use(promise) hook. Under the hood the component suspends during render if data is not ready yet, React catches that and shows the fallback.
tsx
// React 19: pass a promise (created outside render, e.g. in a Server Component)
function Comments({ commentsPromise }) {
const comments = use(commentsPromise); // suspends until resolved
return comments.map((c) => <Comment key={c.id} {...c} />);
}Error Boundary + Suspense
tsx
<ErrorBoundary fallback={<Error />}>
<Suspense fallback={<Skeleton />}>
<UserProfile />
</Suspense>
</ErrorBoundary>Throwing a promise from render is an undocumented implementation detail - don't do it yourself. Use
use(promise)or a library's Suspense hooks. Fetching insideuseEffectdoes not trigger Suspense.
updated
did you know it?