Next.js 15 (App Router + RSC)
Our verdict
Default for application-heavy headless front. Stable RSC after 18 months in production.
Practitioner brief
Why we adopted it
We shipped four client app-shells on Next.js 15 between November 2025 and April 2026. TTFB on a personalised dashboard route streamed from RSC plus PPR (Partial Prerendering, RFC vercel/next.js#54647 from Sebastian Markbåge and Andrew Clark): shell renders at 240 ms while the personalised slot streams in under 800 ms. The same view on the client's previous pages-router app was a 1.6s TTFB wall waiting on a single getServerSideProps. Build times moved from 9 min to 6 min once Turbopack stabilised in next build (15.2). The merchant dashboard bundle fell from 410 KB gzipped to 230 KB after RSC plus selective 'use client'.
When we reach for it
Application-shaped briefs: authenticated dashboards, multi-step checkout, B2B portals with role-based UI, personalised commerce listings that need streamed slots above the fold. Editorial surfaces go to Astro 5. RSC streaming hurts when payloads are small, when one slow upstream nullifies the benefit, or when the team ships 'use client' at the layout level.
Upgrade path
From Next.js 14 pages router the migration is a real project, not a flag flip. Lee Robinson's codemod (@next/codemod app-dir) handles file structure but cannot reason about data-fetching shape; the remaining work is splitting components into server and client tiers and identifying which contexts live below a 'use client' boundary. Plan two engineering weeks for a 40-route app, three with shared cart or auth context. From Remix v2 the move is harder than expected: loaders and actions are a different mental model than RSC plus server actions. What does not transfer: client-only Redux stores, getInitialProps, most pages-router middleware.
Two production scars
First, RSC streaming plus Tailwind v4 plus the edge runtime broke dynamic class generation on a B2B portal in February 2026. Tailwind's JIT scanner runs before the server component renders, so classes interpolated inside RSC payloads are invisible. Fix: pre-declare permutations in safelist in tailwind.config.mjs. Second, server actions plus the OpenNext Cloudflare adapter double-invoked during a checkout retry: a user tapped Pay twice within 800 ms and we charged the card twice. The fix shipped in OpenNext 3.4 (sst/opennext PR #492); we added an idempotency-key cookie so older adapter builds no-op too.
What we are watching
Partial Prerendering GA in Next.js 15.5 lets us stop maintaining two render strategies on the same route (vercel/next.js#54647). React 19.1's useOptimistic plus typed server-action results simplify the cart-update path. We are also watching the after() API for post-response work and OpenNext's progress on Workers parity for ISR (sst/opennext milestone 4.0).
Added: 2026-04-26 · Last reviewed: 2026-04-26