How streaming works
Without streaming, the browser must wait for the server to render the full page before showing anything. With streaming, Next.js sends a static shell immediately and streams in dynamic content as it resolves:<Suspense> boundary in your component tree becomes a streaming chunk.
Two ways to stream
loading.js
Streams an entire route segment. Wraps
page.tsx in a <Suspense> boundary automatically.React Suspense
Streams specific parts of a page. Gives you granular control over which components stream.
With loading.js
Create a loading.js file in the same folder as your page to show a loading state while the page renders:
loading.js is nested inside layout.js and automatically wraps page.js and its children in a <Suspense> boundary.
With <Suspense>
For more granular control, wrap specific components in <Suspense> boundaries:
<Suspense> boundary (<header>) is sent immediately. Content inside streams in when the async work completes.
Creating meaningful loading states
Design loading states that help users understand the app is responding. Good fallback UI examples:- Skeletons: placeholder shapes that match the layout of the final content
- Spinners: for simple, short-duration loads
- Partial content: a cover photo or title before body content loads
Streaming with Server Components
Server Components and<Suspense> work together. An async Server Component inside a <Suspense> boundary streams its content when it resolves:
Streaming data from Server to Client
Pass an unawaited promise from a Server Component to a Client Component, then resolve it with theuse API:
Streaming with Partial Prerendering
When Cache Components is enabled, Next.js uses Partial Prerendering (PPR) by default. The static shell (including<Suspense> fallbacks) is prerendered at build time. Dynamic content streams in at request time:
<Suspense> fallback (<p>Loading preferences...</p>) is included in the static HTML shell sent on the first request. The UserPreferences content streams in once it resolves.