计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
nextjs-ppr-patterns【效率与协作】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 …
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
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/15experimental.pprflag andexport const experimental_pprroute export were removed — seesetup-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 — drivenext buildand a real browser per _debug-boundaries.md.
cacheComponents), or seeing dead experimental.ppr / experimental_ppr code<Suspense> boundaries go, or debugging an Uncached data was accessed outside of <Suspense> build error'use cache', cacheLife, cacheTag, or choosing updateTag / revalidateTag / refresh after a mutation| # | 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 |
| 3 | Caching with 'use cache' | cache- | Directive levels; automatic keys; runtime values as props; pass-through; cacheLife/cacheTag; serverless durability |
| 4 | Runtime APIs & Non-Determinism | runtime- | Async request APIs forcing a boundary; generateStaticParams; connection() for randomness/time |
| 5 | Page Composition Recipes | compose- | Single hole → parallel dashboard → Promise + use() streaming → not opting the whole app out of the shell |
| 6 | Forms, Mutations & Wizards | mutate- | updateTag vs revalidateTag vs refresh; URL-driven wizard steps; <Activity> field preservation |
setup-enable-cache-components — Enable PPR via cacheComponents: true; the experimental.ppr / experimental_ppr flags are removedsetup-dynamic-by-default — Everything renders at request time; caching is opt-in via 'use cache' (and fetch is no longer cached)shell-suspense-is-the-boundary — <Suspense> is the static-shell/dynamic-stream seam, not a spinnershell-wrap-uncached-data — Uncached/runtime reads must be wrapped (or 'use cache'd) or the build errorsshell-suspense-does-not-force-dynamic — Suspense alone does not make synchronous work dynamicshell-place-boundaries-low — Wrap the dynamic leaf, not the whole page, so the shell stays large'use cache'cache-use-cache-directive — Mark static/cacheable work at the function / component / page / layout / file levelcache-keys-are-automatic — Arguments and closures form the cache key; pass varying inputs as argscache-pass-runtime-values-as-props — You can't read cookies()/headers() inside a cached scope; pass values incache-pass-through-children-and-actions — Pass dynamic children and Server Actions through a cached component untouchedcache-set-lifetime-and-tags — cacheLife controls TTL, cacheTag enables on-demand invalidationcache-in-memory-not-durable-serverless — In-memory cache isn't durable on serverless; use 'use cache: remote'runtime-request-apis-force-a-boundary — Async cookies/headers/searchParams/params force a dynamic boundaryruntime-keep-param-routes-static — generateStaticParams keeps [slug] routes in the static shellruntime-gate-nondeterminism-with-connection — Gate Math.random/Date.now/crypto behind connection(), or cache the valuecompose-single-dynamic-hole — The baseline: static shell + one <Suspense> holecompose-parallel-holes — One boundary per widget → parallel streaming, no waterfallcompose-stream-to-client-with-use — Pass an un-awaited Promise and unwrap with use() in a Client Componentcompose-do-not-opt-out-the-shell — Don't defer the whole app to silence a boundary errormutate-updatetag-vs-revalidatetag — updateTag (read-your-writes) vs revalidateTag(tag, profile) (SWR) vs refresh()mutate-wizard-url-driven-steps — URL-driven steps + static chrome + <Activity> field preservationRead 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.
next build and chrome-devtools-mcp (via mcporter); use it when a PPR result surprises you or you're chasing a blocking-route errornextjs — broader Next.js 16 App Router best practices (caching, server components, routing, hygiene)opinionated-nextjs-patterns — full opinionated architecture (data layer, mutations, client boundaries) that uses these PPR patternsreact-fetch-cache-patterns — request orchestration and client-side caching for data-heavy React UIs| File | Description |
|---|---|
| references/_sections.md | Category definitions and ordering |
| references/_debug-boundaries.md | Empirical CSR/SSR boundary & loading debugging (next build + chrome-devtools-mcp via mcporter) |
| assets/templates/_template.md | Template for new rules |
| metadata.json | Version and source references |