Skip to content
asim.dev
Aug 11, 20266 min read

Next.js App Router in Production: Server Components and Streaming Without the Ceremony

Streaming isn't a demo trick — it's how we keep dealer pages feeling instant while CMS data resolves. Notes from real storefronts.

Next.js App Router and React Server Components data streaming architecture

The App Router pitches are always the same three-slide demo: a slow component wrapped in <Suspense>, a skeleton, fireworks. What the demo doesn't show is how streaming behaves when the page is assembled from CMS blocks, the marketing team reorders them weekly, and somebody put a map above the fold. Here's how it plays out on the dealer storefronts I work on at Revnix.

Server components changed the data flow, not just the syntax

The real win of RSC isn't async function Page() — it's deleting the client-side data layer for content pages. Inventory listings, spec tables and CMS sections fetch where they render, and the client bundle only carries what genuinely needs JavaScript:

tsx
// A CMS-driven section — zero client JS
export default async function InventorySection() {
  const vehicles = await getInventory({ category: "compact-tractors" });
  return <InventoryGrid vehicles={vehicles} />;
}

Streaming with intent

We treat Suspense boundaries as an editorial decision, not a technical one. The hero, heading and navigation are static and ship instantly; below-the-fold sections — the heavy inventory grids, the dealer-location map — stream in behind skeleton shells that match final layout closely enough to avoid jank when they land. LCP comes from markup that was already on the server; the slow data never blocks the story.

  • Static shell, streamed body. Every page paints its shell from cached CMS layout immediately.
  • Boundaries where content blocks break. Editors reordering sections shouldn't break streaming — boundaries follow the block system, not the template.
  • Loading states that respect layout. Skeletons sized to the real content avoid the CLS tax that erases your streaming win.

What I'd tell past me

Stop reaching for client components the moment something feels dynamic. Nine times out of ten, the dynamic part is one small interactive island inside a large static section — ship the section as a server component and hydrate the island. Our bundles got dramatically smaller the day we started asking which pixels need JavaScript, instead of which pages.