Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Next.js Streaming, Suspense, and Error Recovery

Last updated: 4 Oct 20268 min read
tutorial
IntermediateBy AITrove Editorial

Streaming lets the server send a ready part of a route while slower work continues behind a Suspense boundary. A route loading file can provide a fallback for the segment; a nested Suspense boundary can isolate one slower panel. The fallback is real user interface, not a performance metric: it must explain what is pending, reserve reasonable layout space, and avoid announcing a false completed state. A server render can still fail after the first chunk has been sent, so an error boundary and retry path need deliberate ownership. Streaming also does not relax permission checks. A slow private metrics panel must be authorized before its data is emitted, and the visible shell must not imply that missing data is zero.

Working case

A permit dashboard renders a heading and filter quickly, but a compliance metrics query takes 1.8 seconds. Without a boundary, the whole route waits and the reviewer sees no progress. A generic skeleton wrapped around the entire page hides an already available case list and loses focus on client transition. The repair keeps the authorized queue in the ready shell and puts only metrics behind a nested Suspense fallback with stable dimensions. When metrics fail, the panel shows an error with a retry route rather than a permanent spinner. The shell never renders metrics from a prior reviewer while the new request is pending.

Implementation boundary

tsx
import { Suspense } from 'react';
import { requireReviewer, permitRepository } from './services';

async function PermitMetrics({ reviewerId }: { reviewerId: string }) {
  const metrics = await permitRepository.metricsForReviewer(reviewerId);
  return <p>{metrics.pending} cases need review</p>;
}

export default async function PermitDashboard() {
  const reviewer = await requireReviewer();
  const cases = await permitRepository.firstPage(reviewer.id, 47);
  return <>
    <h2>Permit queue</h2>
    <p>{cases.length} cases in this page</p>
    <Suspense fallback={<p>Loading review metrics...</p>}>
      <PermitMetrics reviewerId={reviewer.id} />
    </Suspense>
  </>;
}

Separate the fast, independently useful content from the slow server component. Fetch only data needed for the ready shell before rendering it; start unrelated reads early when possible rather than creating a serial waterfall. Put slow metrics behind a nested Suspense boundary and use a descriptive fallback that has the panel's expected dimensions. Keep labels and controls accessible even while content is pending. Define error handling at the segment or component boundary appropriate to the failure. Do not catch every error and replace it with an empty successful dataset; distinguish permission denial, temporary service failure, and a real zero value. Verify the initial chunk, later chunk, client transition, and no-script response under slow and failed metrics.

Cost and boundaries

Streaming improves when useful content can arrive, but it does not reduce the actual CPU or query time of a slow component. Each boundary adds server coordination, payload chunks, and possible layout changes. A fallback that is too high in the tree can hide content that is already ready; boundaries placed around tiny fragments can increase complexity without meaningful progress. An oversized client shell may still require browser JavaScript before interaction. Measure time to first byte, first useful content, final panel completion, layout shift, and task completion. A fast fallback that stays visible for 1.8 seconds may still be a poor experience if it blocks the reviewer's next action.

Failure trace

Delay metrics for 1.8 seconds and verify the queue appears while the panel remains pending. Fail metrics after the shell has streamed; the page should not loop on a spinner or silently display zero. Switch from reviewer 47 to 81 during a slow transition and inspect the shell and panel for stale private values. Disable scripts and verify the server response contains a meaningful queue and eventual metrics or failure state. Trigger a pending form mutation while metrics stream and ensure its status does not get conflated with the metrics fallback. Test keyboard focus through fallback replacement and check that dimensions prevent a large shift.

Verification

  • The useful queue shell appears before the slow panel completes.
  • Failed metrics show a recoverable state, not a false zero.
  • All streamed chunks remain authorized for the current reviewer.

Practice drill

Build a permit dashboard with a fast case queue and a slow authorized metrics panel. Add a nested Suspense fallback that names the panel and keeps its expected height. Use a controlled 1.8-second delay and capture the shell arrival, metric arrival, and focus behavior. Then throw a metrics error and implement a recoverable panel state. Run two concurrent reviewers and inspect all streamed chunks for cross-user data. Move the boundary around the whole page as a negative experiment, compare first useful content, and restore the smallest useful boundary.

Decision note

Stream a genuinely useful shell and isolate slow, authorized work behind a bounded fallback and recoverable error path.

Common Mistakes

  • Wrapping the entire page in a fallback for one slow panel.
  • Treating a loading fallback as an error recovery plan.
  • Displaying stale private metrics during reviewer transition.

Related lessons

Next.js App Router Delivery Boundaries; Next.js Server and Client Component Data Boundary; Next.js Cache Scope and Private Read Invalidation; Next.js Server Functions, Validation, and Operation Identity; Streaming and Large Data Interfaces; React Hydration and Stable First Render.

Apply and check

Build Project: Next.js permit delivery and cache boundary and review Web Development: Next.js App Router boundaries quiz.

web-tech
web-development
Storage details