Next.js App Router - Production Patterns
Version: Next.js 16.1.1
React Version: 19.2.3
Node.js: 20.9+
Last Verified: 2026-01-09
Table of Contents
- When to Use This Skill
- When NOT to Use This Skill
- Security Advisories (December 2025)
- Next.js 16.1 Updates
- Next.js 16 Breaking Changes
- Cache Components & Caching APIs
- Route Handlers (Next.js 16 Updates)
- Proxy vs Middleware
- Parallel Routes - default.js Required
- React 19.2 Features
- Turbopack (Stable in Next.js 16)
- Common Errors & Solutions
- Templates & Resources
When to Use This Skill
Focus: Next.js 16 breaking changes and knowledge gaps (December 2024+).
Use this skill when you need:
- Next.js 16 breaking changes (async params, proxy.ts, parallel routes default.js, removed features)
- 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 (avoid breaking change errors)
- Async route params (
params, searchParams, cookies(), headers() now async)
- Parallel routes with default.js (REQUIRED in Next.js 16)
- React 19.2 features (View Transitions,
useEffectEvent(), React Compiler)
- Turbopack (stable and default in Next.js 16)
- Image defaults changed (TTL, sizes, qualities in Next.js 16)
- Error prevention (25 documented Next.js 16 errors with solutions)
When NOT to Use This Skill
Do NOT use this skill for:
- Cloudflare Workers deployment β Use
cloudflare-nextjs skill instead
- Pages Router patterns β This skill covers App Router ONLY (Pages Router is legacy)
- Authentication libraries β Use
clerk-auth, better-auth, or other auth-specific skills
- Database integration β Use
cloudflare-d1, drizzle-orm-d1, or database-specific skills
- UI component libraries β Use
tailwind-v4-shadcn skill for Tailwind + shadcn/ui
- State management β Use
zustand-state-management, tanstack-query skills
- Form libraries β Use
react-hook-form-zod skill
- Vercel-specific features β Refer to Vercel platform documentation
- Next.js Enterprise features (ISR, DPR) β Refer to Next.js Enterprise docs
- Deployment configuration β Use platform-specific deployment skills
Relationship with Other Skills:
- cloudflare-nextjs: For deploying Next.js to Cloudflare Workers (use BOTH skills together if deploying to Cloudflare)
- tailwind-v4-shadcn: For Tailwind v4 + shadcn/ui setup (composable with this skill)
- clerk-auth: For Clerk authentication in Next.js (composable with this skill)
- better-auth: For Better Auth integration (composable with this skill)
Security Advisories (December 2025)
CRITICAL: Three security vulnerabilities were disclosed in December 2025 affecting Next.js with React Server Components:
| CVE |
Severity |
Affected |
Description |
| CVE-2025-66478 |
CRITICAL (10.0) |
15.x, 16.x |
Server Component arbitrary code execution |
| CVE-2025-55184 |
HIGH |
13.x-16.x |
Denial of Service via malformed request |
| CVE-2025-55183 |
MEDIUM |
13.x-16.x |
Source code exposure in error responses |
Action Required: Upgrade to Next.js 16.1.1 or later immediately.
npm update next
References:
Next.js 16.1 Updates (December 2025)
New in 16.1:
- Turbopack File System Caching (STABLE): Now enabled by default in development
- Next.js Bundle Analyzer: New experimental feature for bundle analysis
- Improved Debugging: Enhanced
next dev --inspect support
- Security Fixes: Addresses CVE-2025-66478, CVE-2025-55184, CVE-2025-55183
Next.js 16 Breaking Changes
IMPORTANT: Next.js 16 introduces multiple breaking changes. Read this section carefully if migrating from Next.js 15 or earlier.
1. Async Route Parameters (BREAKING)
Breaking Change: params, searchParams, cookies(), headers(), draftMode() are now async and must be awaited.
Before (Next.js 15):
export default function Page({ params, searchParams }: {
params: { slug: string }
searchParams: { query: string }
}) {
const slug = params.slug
const query = searchParams.query
return <div>{slug}</div>
}
After (Next.js 16):
export default async function Page({ params, searchParams }: {
params: Promise<{ slug: string }>
searchParams: Promise<{ query: string }>
}) {
const { slug } = await params
const { query } = await searchParams
return <div>{slug}</div>
}
Applies to:
params in pages, layouts, route handlers
searchParams in pages
cookies() from next/headers
headers() from next/headers
draftMode() from next/headers
Migration:
import { cookies, headers } from 'next/headers'
export function MyComponent() {
const cookieStore = cookies()
const headersList = headers()
}
import { cookies, headers } from 'next/headers'
export async function MyComponent() {
const cookieStore = await cookies()
const headersList = await headers()
}
Codemod: Run npx @next/codemod@canary upgrade latest to automatically migrate.
Codemod Limitations (Community-sourced):
The official codemod handles ~80% of async API migrations but misses edge cases:
- Async APIs accessed in custom hooks
- Conditional logic accessing params
- Components imported from external packages
- Complex server actions with multiple async calls
After running the codemod, search for @next-codemod-error comments marking places it couldn't auto-fix.
Manual Migration for Client Components:
'use client';
import { use } from 'react';
export default function ClientComponent({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = use(params);
return <div>{id}</div>;
}
See Template: templates/app-router-async-params.tsx
2. Middleware β Proxy Migration (BREAKING)
Breaking Change: middleware.ts is deprecated in Next.js 16. Use proxy.ts instead.
Why the Change: proxy.ts makes the network boundary explicit by running on Node.js runtime (not Edge runtime). This provides better clarity between edge middleware and server-side proxies.
Migration Steps:
- Rename file:
middleware.ts β proxy.ts
- Rename function:
middleware β proxy
- Update config:
matcher β config.matcher (same syntax)
Before (Next.js 15):
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('x-custom-header', 'value')
return response
}
export const config = {
matcher: '/api/:path*',
}
After (Next.js 16):
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('x-custom-header', 'value')
return r