Senior Next.js / React / TypeScript engineer
Next.js changes fast and model memory is often stale. Before using any API that depends on a version, check references/versions.md. If the installed next has bundled docs (node_modules/next/dist/docs/) or an AGENTS.md generated by Next.js, prefer those over memory.
Step 0: detect versions and conventions (always)
Run bash scripts/detect_versions.sh (read-only), then confirm:
-
package.json+ lockfile (pnpm/npm/yarn/bun) → next, react, typescript, tailwindcss, test tools; use the lockfile's package manager -
next.config.*→cacheComponents,partialPrefetching,reactCompiler,experimental.* - Router:
app/vssrc/app/, anypages/(mixed routers),proxy.tsvs legacymiddleware.ts - Data layer: ORM/SDK, existing
lib/dal.tsorserver-onlymodules, auth library - Styling and UI kit (Tailwind version, CSS Modules, shadcn/ui, design tokens), lint/format (ESLint flat config vs Biome)
- Tests: Vitest/Jest, Testing Library, Playwright; existing CI scripts
Match existing conventions. If the project is on an older major, write code that works on that version.
Defaults (one per decision, with escape hatch)
| Decision | Default | Use the alternative when |
|---|---|---|
| Component type | Server Component | Needs state, effects, browser APIs or event handlers → "use client" at the smallest leaf |
| Caching model (16.x) | Cache Components (cacheComponents: true, 'use cache' + cacheLife/cacheTag) | Existing app on the previous model with no migration planned. Don't half-migrate |
| Mutations | Server Actions + revalidateTag/updateTag/refresh | Public API consumed by other clients → route handler |
| Data access | One server-only data-access layer (DAL) that authenticates and authorizes every read/write | — |
| Request interception | proxy.ts for redirects/rewrites/headers only | Never for authorization as the only check |
| Runtime | Node.js (default) | None in 16.3+: runtime = 'edge' is deprecated/unsupported |
| Forms | <form action={serverAction}> + useActionState + schema validation (zod or project's) on the server | Complex client-only wizards → client form lib already in project |
| Styling | Project's system; for new projects Tailwind CSS v4 with CSS-first config | — |
| Client state | URL search params → server state → React state; global store only if proven necessary | — |
| Memoization | React Compiler if enabled; otherwise memoize only measured hot spots | — |
| Tests | Vitest + Testing Library for units; Playwright for E2E critical flows | Jest already configured |
Workflow A: build a route or feature
- [ ] Step 0 done
- [ ] Route segment: page.tsx, layout.tsx, loading.tsx/Suspense boundaries, error.tsx, not-found.tsx as needed
- [ ] Data via the DAL (server-only), awaited in Server Components; params/searchParams are Promises: `const { id } = await params`
- [ ] Decide what is cached ('use cache' + cacheLife/cacheTag) vs request-time (inside Suspense)
- [ ] Mutations via Server Actions: validate input, re-check authz in the action, revalidate tags
- [ ] Client components minimal; props serializable; no secrets in client bundles (only NEXT_PUBLIC_* reach the browser)
- [ ] Accessibility: semantic elements, labels, focus management, keyboard paths, color contrast
- [ ] Tests for logic + one E2E for the critical path
- [ ] Validate loop passes
Validate → fix → repeat:
pnpm exec tsc --noEmit # or npm/yarn/bun equivalents
pnpm lint # ESLint or Biome per project (next lint was removed in 16)
pnpm test -- --run # Vitest
pnpm build # catches Cache Components / prerender errors
pnpm exec playwright test # when E2E exists
next build errors about uncached data outside <Suspense> are signals, not noise. Fix by adding 'use cache', moving the access into a Suspense boundary, or marking it request-time.
Workflow B: upgrade Next.js (low freedom)
- [ ] Read references/versions.md "Old patterns" and security floors
- [ ] Commit a clean tree; create a branch
- [ ] Run the official upgrade tooling: `npx next@canary upgrade --agent=latest`
(or `npx @next/codemod@canary upgrade latest` if the agent flow is unavailable)
- [ ] Upgrade react, react-dom and @types/react together with next
- [ ] Remove `runtime = 'edge'` exports; rename middleware.ts → proxy.ts (Node runtime)
- [ ] Validate loop; then smoke-test auth, redirects, caching and revalidation by hand
Security first: if the installed version is below a patched floor in versions.md, upgrade next itself (patching React packages alone is not enough).
Workflow C: adopt Cache Components in an existing app
Migrate route by route. Enable cacheComponents (+ partialPrefetching on 16.4+) on a branch, run next build, and fix each reported boundary. See references/caching.md. If installed, the Next.js next-cache-components-adoption skill covers the mechanics in depth; use it alongside this one.
Auth and security rules
- Authorize in the DAL and in every Server Action and route handler. Proxy/middleware checks are only an optimistic redirect. CVE-2025-29927 showed that header-based bypasses of middleware are possible.
- Server Actions are public HTTP endpoints: validate input, check the session, check ownership.
- Import
server-onlyin modules with secrets or DB access; return DTOs (only needed fields) to Client Components. - Details:
references/data-and-auth.md.
Implementing from a design or mockup
- Inventory the design: tokens (color, type scale, spacing, radius), components, states (hover, focus, disabled, loading, empty, error), and breakpoints.
- Map them to the project's existing tokens and components first; add new tokens to the theme, not as one-off values.
- Build static Server Component markup first, then add interactivity at leaves.
- Check at 320px, tablet and desktop widths. Check keyboard and screen-reader labels. Respect
prefers-reduced-motion. - If a browser tool or Playwright is available, screenshot and compare against the mockup; iterate until spacing and typography match.
Review mode
Use references/review-checklist.md. Report each finding as blocker / should-fix / nit, with file:line, the problem, and a concrete code fix. Order findings by severity. Blockers include: auth only in proxy, secrets reaching client bundles, unvalidated Server Action input, a vulnerable next version, and user-specific data inside a shared 'use cache' scope.
Reference map
references/versions.md: versions, security floors, old patterns, verify notesreferences/data-and-auth.md: DAL pattern, Server Action template, session handlingreferences/caching.md: Cache Components rules, ensureStatic, navigation()/prefetch()references/review-checklist.md: impact-ranked rules