Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use this skill when working in a Next.js **App Router** codebase (Next.js 14+) and you need safe patterns for server vs client components, data fetching, routing, and deployment without introducing hydration bugs or accidental client bundles.
.claude/skills/amariahak-next-js-app-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 76% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 52% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 9% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 37% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 26% | 0% |
Use this skill when working in a Next.js App Router codebase (Next.js 14+) and you need safe patterns for server vs client components, data fetching, routing, and deployment without introducing hydration bugs or accidental client bundles.
Rule: start server, add "use client" only when needed.
Use a Server Component when you:
Use a Client Component when you:
useState, useEffect, useRef)onClick, onChange)window, localStorage)Pattern:
page.tsx / layout.tsx typically serverPrefer server fetching:
fetch() (or a server SDK)cache, revalidate, and route segment config intentionallyClient fetching (when needed):
Use Route Handlers (app/api/.../route.ts) when:
Use Server Actions when:
Avoid:
Example: route handler boundary:
ts// app/api/health/route.ts import { NextResponse } from "next/server"; export async function GET() { return NextResponse.json({ ok: true }); }
Example: server action boundary:
ts// app/actions.ts "use server"; export async function updateProfile(_formData: FormData) { // validate input, call server service, redirect or return structured result }
Key ideas:
fetch() caching defaults can surprise you; be explicit when correctness matters.Rules of thumb:
revalidate periodically.Rules:
NEXT_PUBLIC_* is exposed to the browserPattern:
env.ts module for server-only validationRemember:
layout.tsx persists across routes in the segment; state can persist toopage.tsx re-renders per navigationCommon pitfall:
Use generateMetadata() for dynamic metadata, but keep it cheap:
Typical causes:
Date.now(), random IDs, or locale-dependent formatting on serverwindow/document in server componentsFix patterns:
useEffectsuppressHydrationWarning only as a last resortGood structure:
app/ routing + UIsrc/server/ services, DB, auth (server-only)src/shared/ types and utilities safe for bothEnforce boundaries:
Use Atlarix tools to stay tool-driven and avoid guessing Next.js internals:
grep/glob to find route segments, route handlers, server actions, and data fetching patterns; inspect layout.tsx boundaries.read_file + surgical edit tools for small changes, then run the project’s checks (often next lint, vitest, playwright).Useful checks:
"use client" too high in the tree?revalidate behavior.NODE_ENV, and validate output/standalone config if used."use client" to “make it work”revalidate/cache semantics intentionallyNEXT_PUBLIC_ only when needed)| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | fail→fail | 14,648 | 10,561 | -28% | 1 | 1 | 0% | 2,920 | 3,790 | +30% | 0 | 0 | — |
case-03 | pass→pass | 11,068 | 7,818 | -29% | 1 | 1 | 0% | 2,021 | 2,776 | +37% | 0 | 0 | — |
case-01 | fail→pass | 12,266 | 15,588 | +27% | 1 | 1 | 0% | 2,738 | 4,829 | +76% | 0 | 0 | — |
case-04 | pass→pass | 14,152 | 11,222 | -21% | 1 | 1 | 0% | 2,802 | 3,531 | +26% | 0 | 0 | — |
case-05 | fail→fail | 9,725 | 7,289 | -25% | 1 | 1 | 0% | 1,871 | 2,719 | +45% | 0 | 0 | — |
case-06 | pass→pass | 10,437 | 10,806 | +4% | 1 | 1 | 0% | 2,035 | 2,738 | +35% | 0 | 0 | — |
case-07 | fail→pass | 7,897 | 4,607 | -42% | 1 | 1 | 0% | 1,462 | 2,229 | +52% | 0 | 0 | — |
case-08 | pass→pass | 11,595 | 7,601 | -34% | 1 | 1 | 0% | 1,985 | 2,872 | +45% | 0 | 0 | — |
case-09 | pass→pass | 10,501 | 8,299 | -21% | 1 | 1 | 0% | 2,043 | 2,877 | +41% | 0 | 0 | — |
case-10 | pass→pass | 9,201 | 7,547 | -18% | 1 | 1 | 0% | 1,813 | 2,748 | +52% | 0 | 0 | — |
case-11 | pass→pass | 11,380 | 10,427 | -8% | 1 | 1 | 0% | 2,288 | 3,030 | +32% | 0 | 0 | — |
case-12 | pass→pass | 12,672 | 7,015 | -45% | 1 | 1 | 0% | 2,039 | 2,760 | +35% | 0 | 0 | — |
case-13 | pass→pass | 12,471 | 10,037 | -20% | 1 | 1 | 0% | 2,284 | 3,271 | +43% | 0 | 0 | — |
case-14 | pass→pass | 11,326 | 6,263 | -45% | 1 | 1 | 0% | 2,191 | 2,491 | +14% | 0 | 0 | — |
case-15 | fail→pass | 11,763 | 5,064 | -57% | 1 | 1 | 0% | 2,092 | 2,276 | +9% | 0 | 0 | — |
case-16 | pass→pass | 9,261 | 5,894 | -36% | 1 | 1 | 0% | 1,737 | 2,529 | +46% | 0 | 0 | — |
case-17 | pass→pass | 13,977 | 8,104 | -42% | 1 | 1 | 0% | 2,996 | 2,917 | -3% | 0 | 0 | — |
case-18 | pass→pass | 12,695 | 10,341 | -19% | 1 | 1 | 0% | 2,427 | 3,363 | +39% | 0 | 0 | — |
case-19 | pass→pass | 11,343 | 4,015 | -65% | 1 | 1 | 0% | 1,926 | 2,030 | +5% | 0 | 0 | — |
case-20 | pass→pass | 10,339 | 10,621 | +3% | 1 | 1 | 0% | 2,222 | 3,748 | +69% | 0 | 0 | — |
case-21 | pass→pass | 10,132 | 7,400 | -27% | 1 | 1 | 0% | 2,160 | 3,011 | +39% | 0 | 0 | — |
case-22 | pass→pass | 10,295 | 15,077 | +46% | 1 | 1 | 0% | 2,162 | 2,838 | +31% | 0 | 0 | — |
case-23 | pass→pass | 14,940 | 16,546 | +11% | 1 | 1 | 0% | 2,640 | 3,588 | +36% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +13 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.