skills/standards-nextjs/SKILL.md
Use when working in a Next.js 15 App Router project — editing app/, src/app/, components/, server actions, or route handlers. Enforces project conventions for queries, mutations, and data fetching.
npx skillsauth add paulund/skills standards-nextjsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Extract all Prisma reads into src/lib/queries/ (one file per model). Pages call typed query functions — they never import the database client directly. Mutations stay in src/lib/actions/.
Rule: reads in lib/queries/, writes in lib/actions/, no page imports @/lib/db directly. lib/queries/ functions must NOT have 'use server'.
Never throw from server actions — return { error: string } so clients handle failures gracefully.
params and searchParams are Promises in Next.js 15+ — always await them before accessing properties.
Fetch data in Server Components — not in Client Components via useEffect.
Route handlers orchestrate only. They do:
supabase.auth.getUser() → 401 if missing).safeParse → 400 on failure).src/lib/services/ or src/lib/actions/.Route handlers must NOT:
src/lib/queries/).ReadableStream responses inline (move to a service).streamText (move to a service that returns the Response).Rule: if a route handler exceeds ~50 lines, extract logic into src/lib/services/<domain>/<verb>.ts.
Every external I/O call lives behind a service in src/lib/services/<domain>/. Services expose an interface + Null implementation pair (see ADR 003 — Design Patterns). Route handlers, Server Actions, and cron jobs all call services — they never call fetch, streamText, or Prisma for orchestration directly.
For surfaces that handle N variants (platforms, providers, feature flags), drive everything from a single config object. See ADR 003 — Design Patterns. The same config drives both client rendering and server validation — never branch on platform === 'x' more than once across files.
revalidatePath in TestsrevalidatePath throws when called outside a Next.js request scope (integration tests). If a Server Action must call it, guard the specific error:
try {
revalidatePath(routes.projects.accounts.index(projectSlug))
} catch (err) {
if (!(err instanceof Error) || !err.message.includes('static generation store')) throw err
}
Do not wrap revalidatePath in a blanket catch {} — that swallows real bugs.
development
Use when implementing any logic, fixing any bug, or changing any behaviour. Use when you need to prove code works, when a bug report arrives, or when modifying existing functionality. Do NOT use for config changes, data migrations, or dependency updates.
development
Use when starting a new feature, when requirements are unclear, when asked to write code without a clear spec, or before any non-trivial implementation. Do NOT use for trivial bug fixes or one-line changes.
development
Use when you want authoritative, source-cited code free from outdated patterns. Use when building with any framework or library where correctness matters. Detects the stack from dependency files, fetches official documentation, implements following documented patterns, and cites sources for every framework-specific decision.
development
Use when preparing to ship a feature, release, or deployment. Use before merging to main, creating a release, or deploying to production. Do NOT use for CI-only changes or internal refactors that don't reach production.