← Back
Edit Post
Title
Description
Independent streaming, boundary placement strategy, transitions preserve content.
Content
Markdown supported
# Streaming with Suspense Without Suspense, users see blank screens while data loads. Each database query or API call blocks the entire page from rendering. With Suspense, you **design** what users see during loading—skeleton placeholders that communicate progress, not emptiness. Each Suspense boundary is a UX decision: "What should users see while this section loads?" And importantly: "Should these sections wait for each other, or load independently?" ## Independent Streaming Separate boundaries let sections stream in parallel. The tabs can appear while posts are still loading: ```tsx import { Suspense } from 'react'; export default function DashboardPage({ searchParams }) { return ( <div> <Suspense fallback={<PostTabsSkeleton />}> <PostTabs /> </Suspense> <Suspense fallback={<SortButtonSkeleton />}> <SortButton /> </Suspense> <Suspense fallback={<PostListSkeleton />}> <PostList searchParams={searchParams} /> </Suspense> </div> ); } ``` Each component fetches its own data. As each resolves, it streams in and replaces its skeleton—independently of the others. ## Boundary Placement Strategy How you group components inside Suspense boundaries affects perceived performance: | Scenario | Strategy | Effect | |----------|----------|--------| | Related content | Single boundary | Load together, show together | | Independent sections | Separate boundaries | Stream in parallel as ready | | Critical UI (header, nav) | Outside boundaries | Always visible immediately | | Heavy content | Own boundary | Don't block lighter content | ## Nested Boundaries Suspense boundaries can nest. Inner boundaries resolve first: ```tsx <Suspense fallback={<PageSkeleton />}> <Header /> <Suspense fallback={<ContentSkeleton />}> <Content /> </Suspense> </Suspense> ``` The outer skeleton shows briefly, then the header appears, then content streams in. Users see progressive disclosure. ## Transitions Keep Content Visible Here's a crucial distinction: Suspense fallbacks appear on **initial load**. During subsequent navigations wrapped in `startTransition`, React keeps existing content visible instead of showing fallbacks again. ```tsx // Using Link or router.push - content stays visible during navigation <Link href="/dashboard?filter=drafts">Drafts</Link> // The PostList re-fetches but old content shows until new data arrives ``` This is why filtering and sorting feel instant even though data is refetching—the skeleton only shows on first load. For subsequent interactions, the current list stays visible with a subtle pending state.
Published
Save Changes
Cancel