Declarative Loading States
Suspense
`<Suspense>` lets a component "wait" for something — and shows a fallback in the meantime. Used for lazy components and (with the right libraries) data.
What you'll learn
- Wrap lazy components with `<Suspense>`
- Understand what "suspending" means
- Recognize when to use Suspense for data
<Suspense> is a declarative way to say “if anything inside here
isn’t ready yet, show this fallback”. Like an error boundary, but
for “not ready” instead of “broken”.
The Shape
import { Suspense } from "react";
<Suspense fallback={<Spinner />}>
<UserProfile />
</Suspense> If <UserProfile> suspends (waits for data or code that isn’t
ready), React renders <Spinner /> until it’s ready, then swaps in
the real content.
What “Suspending” Means
A component suspends by throwing a promise. React catches the
throw, shows the nearest <Suspense> fallback, and re-renders the
suspended component when the promise resolves.
You don’t write throw promise yourself — you use a library or
hook that does it for you:
React.lazy()for code-splitting (next lesson)- The
use(promise)hook in newer React - Suspense-enabled data libraries (Relay, React Query with Suspense mode)
Code-Splitting Example
const Settings = lazy(() => import("./Settings.jsx"));
function App() {
return (
<Suspense fallback={<p>Loading settings…</p>}>
<Settings />
</Suspense>
);
} The first time <Settings> renders, React kicks off the dynamic
import. The fallback shows until the chunk arrives. After that,
it renders normally.
Suspense for Data (Newer)
With use(promise) and Suspense-enabled data libraries:
function UserProfile({ promise }) {
const user = use(promise); // suspends until resolved
return <p>{user.name}</p>;
}
<Suspense fallback={<Spinner />}>
<UserProfile promise={fetchUser(id)} />
</Suspense> This is the pattern React Server Components and the latest data libraries are leaning into.
Pairing With Error Boundaries
Suspense handles “not ready”; error boundaries handle “broken”. Together they give you a complete UX:
<ErrorBoundary fallback={<ErrorScreen />}>
<Suspense fallback={<Spinner />}>
<Dashboard />
</Suspense>
</ErrorBoundary> Loading → spinner. Error → error screen. Success → real content.
Up Next
The most common Suspense use case — splitting your bundle.
React.lazy →