Next.js App Router - Production Patterns
Version: Next.js 16.0.3 React Version: 19.2.0 Node.js: 20.9+ Last Verified: 2025-11-21
Table of Contents
- [When to Use This Skill](#when-to-use-this-skill)
- [When NOT to Use This Skill](#when-not-to-use-this-skill)
- [Next.js 16 Breaking Changes](#nextjs-16-breaking-changes)
- [Cache Components & Caching APIs](#cache-components--caching-apis)
- [Server Components](#server-components)
- [Server Actions](#server-actions)
- [Route Handlers](#route-handlers)
- [React 19.2 Features](#react-192-features)
- [Metadata API](#metadata-api)
- [Image & Font Optimization](#image--font-optimization)
- [Top 5 Critical Errors](#top-5-critical-errors)
- [Performance Patterns](#performance-patterns)
- [TypeScript Configuration](#typescript-configuration)
When to Use This Skill
Use this skill when you need:
- Next.js 16 App Router patterns (layouts, loading, error boundaries, routing)
- Server Components best practices (data fetching, composition, streaming)
- Server Actions patterns (forms, mutations, revalidation, error handling)
- Cache Components with
"use cache"directive (NEW in Next.js 16) - New caching APIs:
revalidateTag(),updateTag(),refresh()(Updated in Next.js 16) - Migration from Next.js 15 to 16 (async params, proxy.ts, parallel routes)
- Route Handlers (API endpoints, webhooks, streaming responses)
- Proxy patterns (
proxy.tsreplacesmiddleware.tsin Next.js 16) - Async route params (
params,searchParams,cookies(),headers()now async) - Parallel routes with default.js (breaking change in Next.js 16)
- React 19.2 features (View Transitions,
useEffectEvent(), React Compiler) - Metadata API (SEO, Open Graph, Twitter Cards, sitemaps)
- Image optimization (
next/imagewith updated defaults in Next.js 16) - Performance optimization (lazy loading, code splitting, PPR, ISR)
When NOT to Use This Skill
Do NOT use this skill for:
- Cloudflare Workers deployment → Use
cloudflare-nextjsskill instead - Pages Router patterns → This skill covers App Router ONLY (Pages Router is legacy)
- Authentication libraries → Use
clerk-auth,auth-js, or other auth-specific skills - Database integration → Use
cloudflare-d1,drizzle-orm-d1, or database-specific skills - UI component libraries → Use
tailwind-v4-shadcnskill for Tailwind + shadcn/ui - State management → Use
zustand-state-management,tanstack-queryskills - Form libraries → Use
react-hook-form-zodskill
Next.js 16 Breaking Changes
CRITICAL: Next.js 16 has multiple breaking changes. For detailed migration steps, see references/next-16-migration-guide.md.
| Breaking Change | Before | After | |-----------------|--------|-------| | Async params | params.slug | const { slug } = await params | | Async headers | cookies() sync | await cookies() | | Middleware | middleware.ts | proxy.ts (renamed) | | Parallel routes | default.js optional | default.js required | | Caching | Auto-cached fetch | Opt-in with "use cache" | | revalidateTag() | 1 argument | 2 arguments (tag + cacheLife) | | Node.js | 18.x+ | 20.9+ required | | React | 18.x | 19.2+ required |
Quick Fix for Async Params:
// ✅ Next.js 16 pattern
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
return <div>{id}</div>
}
Codemod: bunx @next/codemod@canary upgrade latest
See: references/next-16-migration-guide.md for complete migration guide with examples.
Cache Components & Caching APIs
"use cache" Directive (NEW in Next.js 16)
'use cache'
export async function BlogPosts() {
const posts = await db.posts.findMany()
return posts.map(post => <article key={post.id}>{post.title}</article>)
}
Caching APIs Summary
| API | Purpose | Example | |-----|---------|---------| | "use cache" | Opt-in component/function caching | 'use cache' at top | | revalidateTag() | Invalidate by tag | revalidateTag('posts', 'max') | | updateTag() | Update cache without revalidation | updateTag('posts', newData) | | refresh() | Refresh current page | refresh() | | revalidatePath() | Invalidate by path | revalidatePath('/posts') |
PPR (Partial Prerendering)
// next.config.ts
const config = { experimental: { ppr: true } }
// page.tsx
export const experimental_ppr = true
export default function Page() {
return (
<>
<StaticHeader />
<Suspense fallback={<Skeleton />}>
<DynamicContent />
</Suspense>
</>
)
}
See: references/caching-apis.md for complete caching API reference with ISR, tag-based revalidation, and advanced patterns.
Server Components
Server Components are the default in App Router. They run on the server and can fetch data, access databases, and keep logic server-side.
// app/posts/page.tsx (Server Component by default)
export default async function PostsPage() {
const posts = await db.posts.findMany()
return <div>{posts.map(p => <article key={p.id}>{p.title}</article>)}</div>
}
Streaming with Suspense
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<Header />
<Suspense fallback={<Skeleton />}>
<Posts />
</Suspense>
</div>
)
}
Server vs Client Components
| Server Components | Client Components | |-------------------|-------------------| | Data fetching, DB access | Interactivity (onClick) | | Sensitive logic | React hooks (useState) | | Large dependencies | Browser APIs | | Static content | Real-time updates |
Client Component (requires 'use client'):
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
Server Actions
Server Actions are async functions that run on the server, callable from Client or Server Components.
Basic Server Action
// app/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
await db.posts.create({ data: { title } })
revalidatePath('/posts')
}
Form Handling
// Server Component Form (simplest)
import { createPost } from './actions'
export default function NewPostPage() {
return (
<form action={createPost}>
<input name="title" required />
<button type="submit">Create</button>
</form>
)
}
Available Patterns
| Pattern | Use Case | Reference | |---------|----------|-----------| | Client Form with Loading | useFormState + useFormStatus | templates/server-action-form.tsx | | Error Handling | Return { error } or { success } | references/server-actions-patterns.md | | Optimistic Updates | useOptimistic hook | references/server-actions-patterns.md | | File Upload | FormData + blob storage | references/server-actions-patterns.md | | Redirect After Action | redirect() function | references/server-actions-patterns.md |
See: references/server-actions-patterns.md for error handling, optimistic updates, file uploads, and advanced patterns.
Route Handlers
Route Handlers are the App Router equivalent of API Routes.
// app/api/posts/route.ts
export async function GET(request: Request) {
const posts = await db.posts.findMany()
return Response.json({ posts })
}
export async function POST(request: Request) {
const body = await request.json()
const post = await db.posts.create({ data: body })
return Response.json({ post }, { status: 201 })
}
Dynamic Routes (with async params):
//