Next.js 16 App Router pages mixing static and dynamic content — Partial Prerendering (PPR) under the Cache Components model. Covers enabling it with cacheComponents (the removed experimental.ppr / experimental_ppr flags), the dynamic-by-default rendering inversion, the Suspense static-shell/dynamic-hole boundary, the 'use cache' directive (automatic keys, cacheLife/cacheTag, children/action pass-through, runtime values as props, serverless durability), async runtime APIs and connection() for non-determinism, page composition from a single hole to parallel dashboards to streaming a Promise into a Client Component with use(), and forms/wizards with updateTag read-your-writes and Activity state preservation. Triggers on PPR, cacheComponents, 'use cache', Suspense streaming, partial prerendering, or static-shell work even when not named explicitly.
Next.js 16 App Router pages mixing static and dynamic content — Partial Prerendering (PPR) under the Cache Components model. Covers enabling it with cacheComponents (the removed experimental.ppr / experimental_ppr flags), the dynamic-by-default rendering inversion, the Suspense static-shell/dynamic-hole boundary, the 'use cache' directive (automatic keys, cacheLife/cacheTag, children/action pass-through, runtime values as props, serverless durability), async runtime APIs and connection() for non-determinism, page composition from a single hole to parallel dashboards to streaming a Promise into a Client Component with use(), and forms/wizards with updateTag read-your-writes and Activity state preservation. Triggers on PPR, cacheComponents, 'use cache', Suspense streaming, partial prerendering, or static-shell work even when not named explicitly.
Next.js 16 Partial Prerendering Patterns
Partial Prerendering (PPR) for the Next.js 16 App Router under the Cache Components model — the decisions PPR forces and how to settle them, written so an agent applies them while writing or reviewing code. Contains 21 rules across 6 categories, ordered from easy to complex: enable PPR → understand the static/dynamic boundary → cache → handle runtime data → compose whole pages → build forms and wizards. Each rule corrects a specific wrong default of a model defaulting to Next.js 14/15; there is no rule for things the model already gets right.
Version-specific. This skill targets Next.js 16 (PPR via cacheComponents, React 19.2). The Next.js 14/15 experimental.ppr flag and export const experimental_ppr route export were removed — see setup-enable-cache-components. For migrating an existing app, see the migration guide.
Write, then verify. These rules are for authoring PPR; they can't tell you what actually rendered. To empirically deconstruct the boundary — diff the static shell against the hydrated DOM to find the dynamic holes, locate the 'use client' islands, measure loading, and explain why a route is dynamic — drive next build and a real browser per _debug-boundaries.md.
When to Apply
Building or reviewing a Next.js 16 page that mixes static chrome with personalized, real-time, or per-request content
Enabling or migrating PPR (cacheComponents), or seeing dead experimental.ppr / experimental_ppr code
Deciding where <Suspense> boundaries go, or debugging an Uncached data was accessed outside of <Suspense> build error
Adding 'use cache', cacheLife, cacheTag, or choosing updateTag / revalidateTag / refresh after a mutation
Composing forms, multi-step wizards, dashboards, or streaming server data into interactive Client Components
Empirically verifying or debugging what actually rendered — which parts are in the static shell vs streamed, where the CSR/SSR boundary is, and why a route went dynamic
Rule Categories
#
Category
Prefix
Covers
1
Setup & Mental Model
setup-
Enabling PPR with cacheComponents; the removed experimental flags; dynamic-by-default / opt-in caching inversion
2
The Suspense Boundary
shell-
<Suspense> as the static/dynamic seam; the build error; boundary granularity; what Suspense does not do
Read a reference file when its decision comes up. Each rule names the wrong default it corrects, then shows the canonical way (with an incorrect/correct contrast only where the wrong way is a real trap). If you're starting cold, read setup- first — the rest assumes the dynamic-by-default mental model.
Boundary debugging — empirically deconstruct the static/dynamic boundary and loading with next build and chrome-devtools-mcp (via mcporter); use it when a PPR result surprises you or you're chasing a blocking-route error