all writing
Mar 12, 20261 min read

Rendering strategies in Next.js, in plain words

RSC, streaming, PPR — what each one actually does, and when to reach for it.

When the App Router landed, "rendering strategy" stopped being a deploy-time decision and became a per-component one. Server Components, Suspense streaming, and Partial Prerendering give us a much finer set of dials than getStaticProps ever did — but the choice of which to pull is the new puzzle.

Three knobs, four outcomes

The mental model I keep returning to:

  • Server Component — runs on the server, ships zero JS.
  • Client Component — needs interactivity, costs hydration.
  • Suspense boundary — turns a slow read into streaming HTML.

Combine them and you can express "static shell + streamed personalisation" in a few lines:

export async function Page() {
  return (
    <Layout>
      <Hero />
      <Suspense fallback={<Skeleton />}>
        <PersonalFeed />
      </Suspense>
    </Layout>
  )
}

Where it gets tricky

The cost of an RSC isn't zero. Server roundtrips add latency, and big payloads can hurt time-to-interactive even though they ship no JS.

If your RSC payload is bigger than your hydrated bundle would have been, you've made things worse.

What I'd do differently

Start with a fully static shell. Add Suspense boundaries around fetch calls that depend on the user. Reach for 'use client' only when you genuinely need state or effects.