Next.js App Router
Pathrule4 Rules • 2 Memories • 1 Skill
Rules, memories, and a review skill for teams building on the Next.js App Router. Each piece is pre-scoped so your AI assistant applies it only where it is relevant: server and client boundaries, caching, and secret safety in the app directory, plus a route review checklist at the root.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
/ workspace root
Next.js 16 project conventions and migration notes
nextjs-route-review
app/
Await params, searchParams, cookies, and headers
Server Components by default; push 'use client' to the leaves
Never let server secrets cross the client boundary
Caching is opt-in via 'use cache'; never read request APIs inside it
Cache Components caching and revalidation policy
Rules
4Server Components by default; push 'use client' to the leaves/apphighadvisoryAdd 'use client' only for interactivity, and keep the boundary as low as possible.
| 1 | Components under `app` are Server Components by default. The most common App Router mistake is reaching for `'use client'` too early and shipping a mostly-client app. |
| 2 | |
| 3 | - Add `'use client'` only for components that use state, effects, refs, event handlers, or browser APIs. |
| 4 | - Fetch data in Server Components and pass plain serializable props down; never fetch in client effects (it exposes data sources and adds waterfalls). |
| 5 | - Push the client boundary as far down the tree as possible; a small interactive leaf should not force a whole page client-side. |
| 6 | - Wrap async Server Components that fetch in `<Suspense>` placed ABOVE the async component, not inside it, so the rest of the page streams. |
Never let server secrets cross the client boundary/apphighstrictOnly NEXT_PUBLIC_ vars reach the client; guard with server-only and the taint API.
| 1 | Only `NEXT_PUBLIC_`-prefixed env vars are inlined into the client bundle. Everything else must stay server-side. |
| 2 | |
| 3 | - Never import a database client, secret-bearing config, or server utility from a `'use client'` file. |
| 4 | - Add `import 'server-only'` to modules that must never run on the client so the build fails if a client module imports them. |
| 5 | - Keep API keys and tokens in Server Components, Route Handlers, or Server Actions; pass only the specific fields the UI needs as props. |
| 6 | - For sensitive objects, enable `taint` in `next.config` and use React `experimental_taintObjectReference` / `experimental_taintUniqueValue` as a defensive second layer. Treat it as defense in depth, not the only control: a clone of a tainted object is untainted. |
| 7 | - Remember the RSC payload is sent to the browser: do not over-select rows/objects and pass them whole into Client Components. |
Caching is opt-in via 'use cache'; never read request APIs inside it/apphighstrictNext.js 16 renders dynamically by default; cache explicitly and keep request data out of cached scopes.
| 1 | With `cacheComponents: true`, rendering is dynamic by default and caching is entirely opt-in via the `use cache` directive (the old implicit fetch cache, `experimental.ppr`, and `experimental.dynamicIO` are gone). |
| 2 | |
| 3 | - Add `'use cache'` at the file, component, or function level to cache its output; place it as close to the data fetch as possible, not blanket on the root layout. |
| 4 | - Cached functions CANNOT call `cookies()`, `headers()`, or read `searchParams` directly. Read request values OUTSIDE the cached scope and pass them in as serializable arguments. |
| 5 | - Do not pass Promises of uncached/request data into a `use cache` scope (via props, closure, or shared Maps). Doing so hangs the build with a 50s cache-fill timeout. |
| 6 | - Arguments and return values must be serializable. No class instances, functions (except pass-through `children`/Server Actions), or `URL` instances as arguments. |
| 7 | - Set lifetime with `cacheLife('hours')` and tag with `cacheTag('products')` inside the cached scope. |
Memories
2Cache Components caching and revalidation policy/appuse cache + cacheLife + cacheTag, and the right invalidation API per use case.
| 1 | How we cache and invalidate under Next.js 16 Cache Components. |
| 2 | |
| 3 | - Default to dynamic; opt specific routes/components/functions into caching with `'use cache'`. To prerender a full route, add `'use cache'` to BOTH `layout` and `page`. |
| 4 | - Tag every cached fetch with `cacheTag(...)` so it can be invalidated precisely; set freshness with `cacheLife('hours' | 'days' | 'max' | custom)`. |
| 5 | - Invalidation API by intent: |
| 6 | - `updateTag(tag)` in a Server Action when the user must see their own write immediately (read-your-writes: forms, settings). |
| 7 | - `revalidateTag(tag, 'max')` for background stale-while-revalidate of shared static content. The single-argument form is deprecated; always pass a cacheLife profile. |
| 8 | - `refresh()` in a Server Action to refresh UNCACHED data shown elsewhere (notification counts, live metrics) without touching the cache. |
| 9 | - Start independent requests in parallel to avoid server-side waterfalls. |
| 10 | |
| 11 | See /app rule 'Caching is opt-in via use cache' for the hard constraints (no request APIs inside cached scopes, serializable args, build-hang footgun). |
Next.js 16 project conventions and migration notes/rootproxy.ts, Turbopack, runtime, removed config, and gotchas specific to this repo's upgrade.
| 1 | Project-wide Next.js 16 decisions and migration landmines. |
| 2 | |
| 3 | - Middleware lives in `proxy.ts` (export a `proxy` function), which runs on the Node.js runtime. `middleware.ts` still works for Edge cases but is deprecated. |
| 4 | - Turbopack is the default bundler for dev and build. Only fall back with `next dev --webpack` / `next build --webpack` if a webpack-only plugin forces it. |
| 5 | - Minimums: Node.js 20.9+, TypeScript 5.1+, React 19.2. Node 18 is unsupported. |
| 6 | - Removed config to delete on sight: `experimental.ppr`, `experimental.dynamicIO` (renamed to `cacheComponents`), `serverRuntimeConfig`/`publicRuntimeConfig` (use env vars), AMP, `next lint` (use ESLint/Biome directly; `next build` no longer lints). |
| 7 | - All parallel-route slots now require an explicit `default.js` or the build fails. |
| 8 | - `images.domains` is deprecated; use `images.remotePatterns`. Local `next/image` src with query strings needs `images.localPatterns`. |
| 9 | - For AI-assisted debugging, the Next.js DevTools MCP exposes routing/caching/render context and unified logs. |
| 10 | |
| 11 | See /app for the server/client, secret-safety, async-request-API, and caching rules. |
Skills
1nextjs-route-review/rootPre-merge checklist for a new App Router route or layout on Next.js 16.
| 1 | --- |
| 2 | name: nextjs-route-review |
| 3 | description: Review a new or changed Next.js 16 App Router route, layout, or handler for correctness, caching, and secret safety before merging. |
| 4 | --- |
| 5 | |
| 6 | # Next.js 16 route review |
| 7 | |
| 8 | Run before merging a new route, layout, or Route Handler. |
| 9 | |
| 10 | ## Boundaries |
| 11 | - [ ] `'use client'` is only on components that need interactivity, pushed to the leaves |
| 12 | - [ ] Async Server Components that fetch are wrapped in `<Suspense>` placed ABOVE them |
| 13 | - [ ] No data fetching inside client effects |
| 14 | |
| 15 | ## Async request APIs |
| 16 | - [ ] `params` and `searchParams` are typed as Promises and `await`ed |
| 17 | - [ ] `cookies()`, `headers()`, `draftMode()` are `await`ed |
| 18 | |
| 19 | ## Secret safety |
| 20 | - [ ] No server-only module, DB client, or secret reachable from a `'use client'` file |
| 21 | - [ ] Server-only modules import `server-only` |
| 22 | - [ ] Only the specific fields the UI needs are passed as props (no whole rows/objects into Client Components) |
| 23 | |
| 24 | ## Caching |
| 25 | - [ ] Caching is intentional: `'use cache'` only where it should be, close to the data fetch |
| 26 | - [ ] No `cookies()`/`headers()`/`searchParams` read inside a `use cache` scope; request values passed as serializable args |
| 27 | - [ ] Cached fetches are tagged with `cacheTag` and have an explicit `cacheLife` |
| 28 | - [ ] Mutations use `updateTag` (read-your-writes) or `revalidateTag(tag, profile)` (SWR) or `refresh()` (uncached data) appropriately |
| 29 | |
| 30 | ## Files & metadata |
| 31 | - [ ] `loading.tsx` and `error.tsx` exist where the route fetches data |
| 32 | - [ ] `generateMetadata` or static metadata is exported for SEO |
| 33 | - [ ] Each parallel-route slot has a `default.js` |
| 34 | - [ ] Dynamic params are validated, not trusted blindly |
Why this pattern
App Router teams keep relitigating the same server/client and caching decisions, and AI assistants guess them differently every time.
Built for teams building product UI and routes on the Next.js App Router.
Keeps your assistant from:
- Marking everything 'use client' and shipping a mostly-client app
- Leaking server-only env or modules into the client bundle
- Uncached dynamic rendering with no documented reason
- License
- Apache-2.0
- Version
- 1.0.0
- Updated
- 2026-06-09