All posts/
Next.jsReactPerformance

Next.js 15 App Router: Server Components & Streaming Explained

Dec 5, 2025·12 min read

Everything you need to know about React Server Components, Suspense-based streaming, partial pre-rendering, and the new caching model introduced in Next.js 15.

React Server Components

Server Components run only on the server and never ship JavaScript to the browser. They can directly access databases, file systems, and secrets — with zero bundle-size cost. The key constraint: they cannot use browser APIs, state, or effects.

tsx
// app/users/page.tsx — a Server Component
import { db } from '@/lib/db';

export default async function UsersPage() {
  // Direct DB call — no fetch, no useEffect, no loading spinner
  const users = await db.user.findMany();

  return (
    <ul>
      {users.map(u => <li key={u.id}>{u.name}</li>)}
    </ul>
  );
}

Streaming with Suspense

Wrap slow components in `<Suspense>` and Next.js streams HTML to the browser incrementally. The shell renders immediately while data-heavy sections load in the background — no full-page loading state needed.

tsx
import { Suspense } from 'react';
import UserList from './UserList';
import AnalyticsDashboard from './AnalyticsDashboard';

export default function Page() {
  return (
    <>
      {/* Renders immediately */}
      <h1>Dashboard</h1>

      {/* Streams in when the slow query resolves */}
      <Suspense fallback={<p>Loading analytics…</p>}>
        <AnalyticsDashboard />
      </Suspense>

      <Suspense fallback={<p>Loading users…</p>}>
        <UserList />
      </Suspense>
    </>
  );
}

The New Caching Model

  • Request Memoisation — duplicate `fetch()` calls within one render are de-duplicated automatically
  • Data Cache — fetch responses are persisted across requests and deployments until explicitly revalidated
  • Full Route Cache — static routes are cached at build time and served as pre-rendered HTML
  • Router Cache — client-side cache that stores prefetched route segments for instant navigation

Partial Pre-rendering (PPR)

PPR is the killer feature of Next.js 15: the static shell of a page (nav, hero, layout) is pre-rendered and served instantly from the CDN edge, while dynamic holes stream in from the server. One page, one request, two rendering strategies.

With PPR, 'static vs dynamic' is no longer an all-or-nothing choice per route — it's a per-component decision.
PreviousMastering TypeScript Generics: From Basics to Advanced PatternsNextRedis Beyond Caching: Pub/Sub, Streams, and Rate Limiting