React

Verified against Claude Code · 2026-07-24

Design Suspense boundaries that don't collapse into one big spinner

Places Suspense boundaries around real independently-loading regions of a page so it reveals content progressively, instead of either waterfalling requests behind nested boundaries or blocking everything behind one top-level spinner.

Claude CodeCursorClaudeChatGPT4 fillable variables

The prompt

Ready to copy — highlighted parts are example details you can swap.

You are designing where Suspense boundaries go on a data-heavy page, so the page reveals content progressively instead of either waterfalling requests or blocking everything behind one top-level spinner.

PAGE
A creator profile page: header with avatar and follow button, a stats row, a paginated grid of posts, and a "similar creators" rail below the fold.

DATA DEPENDENCIES
Profile header needs a fast /profile call; stats row needs a separate, usually slower /stats call; posts grid needs /posts?page=1, independent of the others; similar creators needs a slow recommendation call.

FETCHING APPROACH
TanStack Query hooks with suspense: true, called from route-level loader components.

NON-CRITICAL SECTIONS
The "similar creators" rail — nice to have, but nobody visits this page to see it.

DESIGN RULES
Start every request as early as possible — as close to the route or component entry point as the fetching approach allows — even when the component that renders the result sits deep in the tree behind its own Suspense boundary. Never gate the start of a fetch behind the boundary that will display its result; that ordering is what turns Suspense into a self-inflicted waterfall. Use one Suspense boundary per independently-loading region of the UI, not one boundary per component — group things that should visually appear together and can tolerate loading together into the same boundary. Never put a slow, non-critical section such as comments, related items, or a sidebar widget in the same boundary as the primary content; an unrelated slow request should never delay what the user actually came to the page for. Name a fallback for every boundary that matches the shape of what it is replacing — a skeleton with the right layout and approximate dimensions, not a generic centered spinner — so the loading state does not cause a visible layout jump when real content arrives. Call out any place where two sibling boundaries would race in a way that produces a confusing partial layout — one section rendering above another that hasn't loaded yet in an order that looks broken rather than intentional — and resolve it by grouping them into one boundary instead of leaving the race in place. Pair every data-fetching Suspense boundary with an error boundary scoped immediately around it, not shared broadly across unrelated boundaries — a rejected promise inside a Suspense boundary propagates as a thrown error that the nearest error boundary must catch, and without one scoped to just that region, a single failed request can crash the whole page instead of degrading only its own section. Keep the key prop on a boundary's contents stable across re-renders that should not restart it — an accidentally changing key remounts the subtree and re-triggers its fallback even though the underlying data never actually became unavailable.

OUTPUT FORMAT
1. A tree diagram, text is fine, showing where each Suspense boundary sits and exactly what is inside it.
2. For each boundary: what triggers its fallback, and a description of what the fallback should look like.
3. Any request-waterfall risk you found in the original data-fetching approach and how the boundary placement avoids or fixes it.
4. Anything from the non-critical sections list you deliberately isolated into its own boundary and why grouping it with primary content would have been the wrong call.

Customize

Optional — swap in your own details for the highlighted parts above.

Why this works

The "start the fetch before the boundary that displays it" rule targets React's own documented render-as-you-fetch guidance directly: if a component only starts fetching inside its own effect or on mount, and that same component is the thing hidden by a Suspense fallback, the fetch physically cannot begin until the boundary above it has already resolved something else first — that is a self-inflicted waterfall, and it is the single most common Suspense mistake in real codebases, not a hypothetical one. Grouping boundaries by what should visually appear together, rather than one boundary per component, is the documented fix for the "popcorn" effect, where unrelated pieces of a page pop in one at a time in a distracting order nobody actually designed. Separating a slow, non-critical section like a recommendations rail into its own isolated boundary is what actually delivers the promised benefit of Suspense — a fast primary experience — instead of accidentally recreating one big spinner because the primary content and the slowest request on the page happen to share a boundary. Requiring a fallback shaped like the real content, not a generic spinner, is a direct response to a measurable UX cost: a skeleton with the wrong dimensions still causes the same layout shift a spinner does the moment real content swaps in, defeating half the point of a progressive-loading design. Pairing each Suspense boundary with its own scoped error boundary reflects a documented and easy-to-miss detail of how Suspense actually behaves: a rejected fetch inside a boundary surfaces to React as a thrown error, not as a resolved-with-error state, so without an error boundary sitting at the same scope, that single failure propagates upward until it hits whatever error boundary is nearest — often the entire page's root boundary — turning one section's network hiccup into a total page crash instead of a contained, section-level failure message. The key-stability rule addresses a specific and common regression introduced by refactors that add a Suspense boundary without auditing what feeds its key prop: a key that changes for an unrelated reason, such as a parent re-rendering with a freshly-created object identity, remounts the subtree and re-shows the fallback even though the data the component already had was still perfectly valid, manufacturing a flash of loading state a user never should have seen.

What you get back

<ProfilePage> <Suspense fallback={<HeaderSkeleton />}> header + stats, fetch started at route entry <ProfileHeader /> <StatsRow /> </Suspense> <Suspense fallback={<PostsGridSkeleton />}> independent, can resolve before or after header <PostsGrid page={1} /> </Suspense> <Suspense fallback={<SimilarCreatorsSkeleton />}> slow recommendation call, isolated so it never blocks the above <SimilarCreators /> </Suspense> </ProfilePage> Waterfall risk: header and stats were originally two separate effects, each starting its fetch inside its own component — moved to start both at route entry instead, merged into one boundary since they are requested together and always appear together, avoiding a two-step pop-in for content that reads as one visual unit.

Verified against

Claude Code Sonnet 4.6 · 2026-07-24

Claude Sonnet 4.6 · 2026-07-31

Changelog

  • 2026-07-24 Initial publish, verified against Claude Code and Claude on Sonnet 4.6 with TanStack Query.

Need this built into your business?

If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.

EXPLORE CUSTOM SOFTWARE
All React prompts

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY