Stop 08 · ReactTopics · 15/20

Suspense

Part of the Frontend Developer Roadmap

In short

Instead of writing if (loading) checks in every component, Suspense lets you declare one loading screen for a whole section while its code or data gets ready.

A component that lets you 'wait' for something (a lazy component, or async data with newer patterns) and show a fallback UI in the meantime.

The jaadu mascot munches papad at a restaurant table as a covered dish comes out of the kitchen.

Think of it like this

Papad before the main course

The restaurant puts papad on your table while the kitchen finishes your order, so you're not staring at an empty plate. Suspense shows its fallback, your papad, until everything inside is ready, then swaps in the real thing.

Watch it happen

Waiting with a fallback

Page
Suspense
Loading posts...
Posts
<ul> list

1/5Page renders a Suspense boundary with a 'Loading posts...' fallback.

The big ideas

What you need to know

fallback is the placeholder

<Suspense fallback={<Spinner />}> shows Spinner while anything inside it is still loading.

Works with code and data

It waits for lazy components, and for data from Suspense-ready tools like use(), TanStack Query's useSuspenseQuery or Next.js.

Nest for finer control

Each Suspense handles only what's inside it, so the header can appear while comments are still loading.

Try skeletons

A grey block shaped like the content stops the layout jumping when the real thing arrives.

See it in code

Walk through the code

tsx
1import { Suspense, use } from "react";2 3type Post = { id: number; title: string };4 5function Posts({ postsPromise }: { postsPromise: Promise<Post[]> }) {6  const posts = use(postsPromise); // suspends until resolved7  return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;8}9 10export default function Page({ postsPromise }: { postsPromise: Promise<Post[]> }) {11  return (12    <Suspense fallback={<p>Loading posts...</p>}>13      <Posts postsPromise={postsPromise} />14    </Suspense>15  );16}

1/3use() reads the promise. If it's still pending, Posts suspends right here.

use() pauses Posts until the promise resolves, and Suspense shows the fallback meanwhile. The promise must be cached or come from a server component, not created on every render.

Save yourself the pain

Common mistakes

Oops

Calling fetch() inside the same component that passes its promise to use().

Do this instead

Every render makes a new promise, so it suspends again and again. Create it once, in a server component or a caching library.

Oops

One Suspense around the whole page, so everything waits for the slowest part.

Do this instead

Wrap slow sections in their own Suspense so the fast parts show up first.

Your mission

Two loading zones

~20 min

Quick check

One Suspense wraps a Header and a lazy Comments component. Comments is still downloading. What do you see?

Go deeper

  1. 1Wrap a `React.lazy()` component in `<Suspense fallback={...}>`.
  2. 2Build a simple loading skeleton as the fallback.
  3. 3Read how frameworks like Next.js use Suspense for streaming server-rendered UI.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge