# Jackspace Claudeskillz Nextjs

> Next.js App Router - Production Patterns

- Skill: `tomevault-io/jackspace-claudeskillz-nextjs` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/jackspace-claudeskillz-nextjs`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/jackspace-claudeskillz-nextjs/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/jackspace-claudeskillz-nextjs

---


# Next.js App Router - Production Patterns

**Version**: Next.js 16.0.0
**React Version**: 19.2.0
**Node.js**: 20.9+
**Last Verified**: 2025-10-24

---

## Table of Contents

1. [When to Use This Skill](#when-to-use-this-skill)
2. [When NOT to Use This Skill](#when-not-to-use-this-skill)
3. [Next.js 16 Breaking Changes](#nextjs-16-breaking-changes)
4. [Cache Components & Caching APIs](#cache-components--caching-apis)
5. [Server Components](#server-components)
6. [Server Actions](#server-actions)
7. [Route Handlers](#route-handlers)
8. [Proxy vs Middleware](#proxy-vs-middleware)
9. [Parallel Routes & Route Groups](#parallel-routes--route-groups)
10. [React 19.2 Features](#react-192-features)
11. [Metadata API](#metadata-api)
12. [Image & Font Optimization](#image--font-optimization)
13. [Performance Patterns](#performance-patterns)
14. [TypeScript Configuration](#typescript-configuration)
15. [Common Errors & Solutions](#common-errors--solutions)
16. [Templates Reference](#templates-reference)
17. [Additional Resources](#additional-resources)

---

## 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.ts` replaces `middleware.ts` in 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/image` with updated defaults in Next.js 16)
- **Font optimization** (`next/font` patterns)
- **Turbopack configuration** (stable and default in Next.js 16)
- **Performance optimization** (lazy loading, code splitting, PPR, ISR)
- **TypeScript configuration** (strict mode, path aliases)

---

## 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`, `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-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)
- **auth-js**: For Auth.js (NextAuth) integration (composable with this skill)

---

## 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)**:
```typescript
// ❌ This no longer works in Next.js 16
export default function Page({ params, searchParams }: {
  params: { slug: string }
  searchParams: { query: string }
}) {
  const slug = params.slug // ❌ Error: params is a Promise
  const query = searchParams.query // ❌ Error: searchParams is a Promise
  return <div>{slug}</div>
}
```

**After (Next.js 16)**:
```typescript
// ✅ Correct: await params and searchParams
export default async function Page({ params, searchParams }: {
  params: Promise<{ slug: string }>
  searchParams: Promise<{ query: string }>
}) {
  const { slug } = await params // ✅ Await the promise
  const { query } = await searchParams // ✅ Await the promise
  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**:
```typescript
// ❌ Before
import { cookies, headers } from 'next/headers'

export function MyComponent() {
  const cookieStore = cookies() // ❌ Sync access
  const headersList = headers() // ❌ Sync access
}

// ✅ After
import { cookies, headers } from 'next/headers'

export async function MyComponent() {
  const cookieStore = await cookies() // ✅ Async access
  const headersList = await headers() // ✅ Async access
}
```

**Codemod**: Run `npx @next/codemod@canary upgrade latest` to automatically migrate.

**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**:

1. **Rename file**: `middleware.ts` → `proxy.ts`
2. **Rename function**: `middleware` → `proxy`
3. **Update config**: `matcher` → `config.matcher` (same syntax)

**Before (Next.js 15)**:
```typescript
// middleware.ts ❌ Deprecated in Next.js 16
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)**:
```typescript
// proxy.ts ✅ New in 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 response
}

export const config = {
  matcher: '/api/:path*',
}
```

**Note**: `middleware.ts` still works in Next.js 16 but is deprecated. Migrate to `proxy.ts` for future compatibility.

**See Template**: `templates/proxy-migration.ts`
**See Reference**: `references/proxy-vs-middleware.md`

---

### 3. Parallel Routes Require `default.js` (BREAKING)

**Breaking Change**: Parallel routes now **require** explicit `default.js` files. Without them, routes will fail during soft navigation.

**Structure**:
```
app/
├── @auth/
│   ├── login/
│   │   └── page.tsx
│   └── default.tsx    ← REQUIRED in Next.js 16
├── @dashboard/
│   ├── overview/
│   │   └── page.tsx
│   └── default.tsx    ← REQUIRED in Next.js 16
└── layout.tsx
```

**Layout**:
```typescript
// app/layout.tsx
export default function Layout({
  children,
  auth,
  dashboard,
}: {
  children: React.ReactNode
  auth: React.ReactNode
  dashboard: React.ReactNode
}) {
  return (
    <html>
      <body>
        {auth}
        {dashboard}
        {children}
      </body>
    </html>
  )
}
```

**Default Fallback** (REQUIRED):
```typescript
// app/@auth/default.tsx
export default function AuthDefault() {
  return null // or <Skeleton /> or redirect
}

// app/@dashboard/default.tsx
export default function DashboardDefault() {
  return null
}
```

**Why Required**: Next.js 16 changed how parallel routes handle soft navigation. Without `default.js`, unmatched slots will error during client-side navigation.

**See Template**: `templates/parallel-routes-with-default.tsx`

---

### 4. Removed Features (BREAKING)

**The following features are REMOVED in Next.js 16**:

1. **AMP Support** - Entirely removed. Migrate to standard pages.
2. **`next lint` command** - Use ESLint or Biome directly.
3. **`serverRuntimeConfig` and `publicRuntimeConfig`** - Use environment variables instead.
4. **`experimental.ppr` flag** - Evolved into Cache Components. Use `"use cache"` directive.
5. **Automatic `scroll-behavior: smooth`** - Add manually if needed.
6. **Node.js 18 support** - Minimum version is now **20.9+**.

**Migration**:
- **AMP**: Convert AMP pages to standard pages or use separate AMP implementation.
- **Linting**: Run `npx eslint .` or `npx biome lint .` directly.
- **Config**: Replace `serverRuntimeConfig` with `process.env.VARIABLE`.
- **PPR**: Migrate from `experimental.ppr` to `"use cache"` directive (see Cache Components section).

---

### 5. Version Requirements (BREAKING)

**Next.js 16 requires**:

- **Node.js**: 20.9+ (Node.js 18 no longer supported)
- **TypeScript**: 5.1+ (if using TypeScript)
- **React**: 19.2+ (automatically installed with Next.js 16)
- **Browsers**: Chrome 111+, Safari 16.4+, Firefox 109+, Edge 111+

**Check Versions**:
```bash
node --version    # Should be 20.9+
npm --version     # Should be 10+
npx next --version # Should be 16.0.0+
```

**Upgrade Node.js**:
```bash
# Using nvm
nvm install 20
nvm use 20
nvm alias default 20

# Using Homebrew (macOS)
brew install node@20

# Using apt (Ubuntu/Debian)
sudo apt update
sudo apt install nodejs npm
```

---

### 6. Image Defaults Changed (BREAKING)

**Next.js 16 changed `next/image` defaults**:

| Setting | Next.js 15 | Next.js 16 |
|---------|------------|------------|
| **TTL** (cache duration) | 60 seconds | 4 hours |
| **imageSizes** | `[16, 32, 48, 64, 96, 128, 256, 384]` | `[640, 750, 828, 1080, 1200]` (reduced) |
| **qualities** | `[75, 90, 100]` | `[75]` (single quality) |

**Impact**:
- Images cache longer (4 hours vs 60 seconds)
- Fewer image sizes generated (smaller builds, but less granular)
- Single quality (75) generated instead of multiple

**Override Defaults** (if needed):
```typescript
// next.config.ts
import type { NextConfig } from 'next'

const config: NextConfig = {
  images: {
    minimumCacheTTL: 60, // Revert to 60 seconds
    deviceSizes: [640, 750, 828, 1080, 1200, 1920], // Add larger sizes
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], // Restore old sizes
    formats: ['image/webp'], // Default
  },
}

export default config
```

**See Template**: `templates/image-optimization.tsx`

---

## Cache Components & Caching APIs

**NEW in Next.js 16**: Cache Components introduce **opt-in caching** with the `"use cache"` directive, replacing implicit caching from Next.js 15.

### 1. Overview

**What Changed**:
- **Next.js 15**: Implicit caching (all Server Components cached by default)
- **Next.js 16**: Opt-in caching with `"use cache"` directive

**Why the Change**: Explicit caching gives developers more control and makes caching behavior predictable.

**Cache Components enable**:
- Component-level caching (cache specific components, not entire pages)
- Function-level caching (cache expensive computations)
- Page-level caching (cache entire pages selectively)
- **Partial Prerendering (PPR)** - Cache static parts, render dynamic parts on-demand

---

### 2. `"use cache"` Directive

**Syntax**: Add `"use cache"` at the top of a Server Component, function, or route handler.

**Component-level caching**:
```typescript
// app/components/expensive-component.tsx
'use cache'

export async function ExpensiveComponent() {
  const data = await fetch('https://api.example.com/data')
  const json = await data.json()

  return (
    <div>
      <h1>{json.title}</h1>
      <p>{json.description}</p>
    </div>
  )
}
```

**Function-level caching**:
```typescript
// lib/data.ts
'use cache'

export async function getExpensiveData(id: string) {
  const response = await fetch(`https://api.example.com/items/${id}`)
  return response.json()
}

// Usage in component
import { getExpensiveData } from '@/lib/data'

export async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  const product = await getExpensiveData(id) // Cached

  return <div>{product.name}</div>
}
```

**Page-level caching**:
```typescript
// app/blog/[slug]/page.tsx
'use cache'

export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json())
  return posts.map((post: { slug: string }) => ({ slug: post.slug }))
}

export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params
  const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json())

  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  )
}
```

**See Template**: `templates/cache-component-use-cache.tsx`

---

### 3. Partial Prerendering (PPR)

**PPR** allows caching static parts of a page while rendering dynamic parts on-demand.

**Pattern**:
```typescript
// app/dashboard/page.tsx

// Static header (cached)
'use cache'
async function StaticHeader() {
  return <header>My App</header>
}

// Dynamic user info (not cached)
async function DynamicUserInfo() {
  const cookieStore = await cookies()
  const userId = cookieStore.get('userId')?.value
  const user = await fetch(`/api/users/${userId}`).then(r => r.json())

  return <div>Welcome, {user.name}</div>
}

// Page combines both
export default function Dashboard() {
  return (
    <div>
      <StaticHeader /> {/* Cached */}
      <DynamicUserInfo /> {/* Dynamic */}
    </div>
  )
}
```

**When to Use PPR**:
- Page has both static and dynamic content
- Want to cache layout/header/footer but render user-specific content
- Need fast initial load (static parts) + personalization (dynamic parts)

**See Reference**: `references/cache-components-guide.md`

---

### 4. `revalidateTag()` - Updated API

**BREAKING CHANGE**: `revalidateTag()` now requires a **second argument** (`cacheLife` profile) for stale-while-revalidate behavior.

**Before (Next.js 15)**:
```typescript
import { revalidateTag } from 'next/cache'

export async function updatePost(id: string) {
  await fetch(`/api/posts/${id}`, { method: 'PATCH' })
  revalidateTag('posts') // ❌ Only one argument in Next.js 15
}
```

**After (Next.js 16)**:
```typescript
import { revalidateTag } from 'next/cache'

export async function updatePost(id: string) {
  await fetch(`/api/posts/${id}`, { method: 'PATCH' })
  revalidateTag('posts', 'max') // ✅ Second argument required in Next.js 16
}
```

**Built-in Cache Life Profiles**:
- `'max'` - Maximum staleness (recommended for most use cases)
- `'hours'` - Stale after hours
- `'days'` - Stale after days
- `'weeks'` - Stale after weeks
- `'default'` - Default cache behavior

**Custom Cache Life Profile**:
```typescript
revalidateTag('posts', {
  stale: 3600, // Stale after 1 hour (seconds)
  revalidate: 86400, // Revalidate every 24 hours (seconds)
  expire: false, // Never expire (optional)
})
```

**Pattern in Server Actions**:
```typescript
'use server'

import { revalidateTag } from 'next/cache'

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string
  const content = formData.get('content') as string

  await fetch('/api/posts', {
    method: 'POST',
    body: JSON.stringify({ title, content }),
  })

  revalidateTag('posts', 'max') // ✅ Revalidate with max staleness
}
```

**See Template**: `templates/revalidate-tag-cache-life.ts`

---

### 5. `updateTag()` - NEW API (Server Actions Only)

**NEW in Next.js 16**: `updateTag()` provides **read-your-writes semantics** for Server Actions.

**What it does**:
- Expires cache immediately
- Refreshes data within the same request
- Shows updated data right after mutation (no stale data)

**Difference from `revalidateTag()`**:
- `revalidateTag()`: **Stale-while-revalidate** (shows stale data, revalidates in background)
- `updateTag()`: **Immediate refresh** (expires cache, fetches fresh data in same request)

**Use Case**: Forms, user settings, or any mutation where user expects immediate feedback.

**Pattern**:
```typescript
'use server'

import { updateTag } from 'next/cache'

export async function updateUserProfile(formData: FormData) {
  const name = formData.get('name') as string
  const email = formData.get('email') as string

  // Update database
  await db.users.update({ name, email })

  // Immediately refresh cache (read-your-writes)
  updateTag('user-profile')

  // User sees updated data immediately (no stale data)
}
```

**When to Use**:
- **`updateTag()`**: User settings, profile updates, critical mutations (immediate feedback)
- **`revalidateTag()`**: Blog posts, product listings, non-critical updates (background revalidation)

**See Template**: `templates/server-action-update-tag.ts`

---

### 6. `refresh()` - NEW API (Server Actions Only)

**NEW in Next.js 16**: `refresh()` refreshes **uncached data only** (complements client-side `router.refresh()`).

**When to Use**:
- Refresh dynamic data without affecting cached data
- Complement `router.refresh()` on server side

**Pattern**:
```typescript
'use server'

import { refresh } from 'next/cache'

export async function refreshDashboard() {
  // Refresh uncached data (e.g., real-time metrics)
  refresh()

  // Cached data (e.g., static header) remains cached
}
```

**Difference from `revalidateTag()` and `updateTag()`**:
- `refresh()`: Only refreshes **uncached** data
- `revalidateTag()`: Revalidates **specific tagged** data (stale-while-revalidate)
- `updateTag()`: Immediately expires and refreshes **specific tagged** data

**See Reference**: `references/cache-components-guide.md`

---

## Server Components

Server Components are React components that render on the server. They enable efficient data fetching, reduce client bundle size, and improve performance.

### 1. Server Component Basics

**Default Behavior**: All components in the App Router are Server Components by default (unless marked with `'use client'`).

**Example**:
```typescript
// app/posts/page.tsx (Server Component by default)
export default async function PostsPage() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json())

  return (
    <div>
      {posts.map((post: { id: string; title: string }) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
        </article>
      ))}
    </div>
  )
}
```

**Rules**:
- ✅ Can `await` promises directly in component body
- ✅ Can access `cookies()`, `headers()`, `draftMode()` (with `await`)
- ✅ Can use Node.js APIs (fs, path, etc.)
- ❌ Cannot use browser APIs (window, document, localStorage)
- ❌ Cannot use React hooks (`useState`, `useEffect`, etc.)
- ❌ Cannot use event handlers (`onClick`, `onChange`, etc.)

---

### 2. Data Fetching in Server Components

**Pattern**: Use `async/await` directly in component body.

```typescript
// app/products/[id]/page.tsx
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params

  // Fetch data directly in component
  const product = await fetch(`https://api.example.com/products/${id}`)
    .then(r => r.json())

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <p>${product.price}</p>
    </div>
  )
}
```

**Parallel Data Fetching**:
```typescript
export default async function Dashboard() {
  // Fetch in parallel with Promise.all
  const [user, posts, comments] = await Promise.all([
    fetch('/api/user').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/comments').then(r => r.json()),
  ])

  return (
    <div>
      <UserInfo user={user} />
      <PostsList posts={posts} />
      <CommentsList comments={comments} />
    </div>
  )
}
```

**Sequential Data Fetching** (when needed):
```typescript
export default async function UserPosts({ params }: { params: Promise<{ userId: string }> }) {
  const { userId } = await params

  // Fetch user first
  const user = await fetch(`/api/users/${userId}`).then(r => r.json())

  // Then fetch user's posts (depends on user data)
  const posts = await fetch(`/api/posts?userId=${user.id}`).then(r => r.json())

  return (
    <div>
      <h1>{user.name}'s Posts</h1>
      <ul>
        {posts.map((post: { id: string; title: string }) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  )
}
```

**See Template**: `templates/server-component-streaming.tsx`

---

### 3. Streaming with Suspense

**Pattern**: Wrap slow components in `<Suspense>` to stream content as it loads.

```typescript
import { Suspense } from 'react'

// Fast component (loads immediately)
async function Header() {
  return <header>My App</header>
}

// Slow component (takes 2 seconds)
async function SlowData() {
  await new Promise(resolve => setTimeout(resolve, 2000))
  const data = await fetch('/api/slow-data').then(r => r.json())
  return <div>{data.content}</div>
}

// Page streams content
export default function Page() {
  return (
    <div>
      <Header /> {/* Loads immediately */}

      <Suspense fallback={<div>Loading...</div>}>
        <SlowData /> {/* Streams when ready */}
      </Suspense>
    </div>
  )
}
```

**When to Use Streaming**:
- Page has slow API calls
- Want to show UI immediately (don't wait for all data)
- Improve perceived performance

**See Reference**: `references/server-components-patterns.md`

---

### 4. Server vs Client Components

**When to Use Server Components** (default):
- Fetch data from APIs/databases
- Access backend resources (files, environment variables)
- Keep large dependencies on server (reduce bundle size)
- Render static content

**When to Use Client Components** (`'use client'`):
- Need React hooks (`useState`, `useEffect`, `useContext`)
- Need event handlers (`onClick`, `onChange`, `onSubmit`)
- Need browser APIs (`window`, `localStorage`, `navigator`)
- Need third-party libraries that use browser APIs (charts, maps, etc.)

**Pattern**: Use Server Components by default, add `'use client'` only when needed.

```typescript
// app/components/interactive-button.tsx
'use client' // Client Component

import { useState } from 'react'

export function InteractiveButton() {
  const [count, setCount] = useState(0)

  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  )
}

// app/page.tsx (Server Component)
import { InteractiveButton } from './components/interactive-button'

export default async function Page() {
  const data = await fetch('/api/data').then(r => r.json())

  return (
    <div>
      <h1>{data.title}</h1>
      <InteractiveButton /> {/* Client Component inside Server Component */}
    </div>
  )
}
```

**Composition Rules**:
- ✅ Server Component can import Client Component
- ✅ Client Component can import Client Component
- ✅ Client Component can render Server Component as children (via props)
- ❌ Client Component cannot import Server Component directly

**See Reference**: `references/server-components-patterns.md`

---

## Server Actions

Server Actions are asynchronous functions that run on the server. They enable server-side mutations, form handling, and data revalidation.

### 1. Server Action Basics

**Definition**: Add `'use server'` directive to create a Server Action.

**File-level Server Actions**:
```typescript
// app/actions.ts
'use server'

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string
  const content = formData.get('content') as string

  // Mutate database
  await db.posts.create({ title, content })

  // Revalidate cache
  revalidateTag('posts', 'max')
}
```

**Inline Server Actions**:
```typescript
// app/posts/new/page.tsx
export default function NewPostPage() {
  async function createPost(formData: FormData) {
    'use server'

    const title = formData.get('title') as string
    const content = formData.get('content') as string

    await db.posts.create({ title, content })
    revalidateTag('posts', 'max')
  }

  return (
    <form action={createPost}>
      <input name="title" />
      <textarea name="content" />
      <button type="submit">Create Post</button>
    </form>
  )
}
```

**See Template**: `templates/server-actions-form.tsx`

---

### 2. Form Handling

**Basic Form**:
```typescript
// app/components/create-post-form.tsx
import { createPost } from '@/app/actions'

export function CreatePostForm() {
  return (
    <form action={createPost}>
      <label>
        Title:
        <input type="text" name="title" required />
      </label>

      <label>
        Content:
        <textarea name="content" required />
      </label>

      <button type="submit">Create Post</button>
    </form>
  )
}
```

**With Loading State** (useFormStatus):
```typescript
'use client'

import { useFormStatus } from 'react-dom'
import { createPost } from '@/app/actions'

function SubmitButton() {
  const { pending } = useFormStatus()

  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Creating...' : 'Create Post'}
    </button>
  )
}

export function CreatePostForm() {
  return (
    <form action={createPost}>
      <input type="text" name="title" required />
      <textarea name="content" required />
      <SubmitButton />
    </form>
  )
}
```

**With Validation**:
```typescript
// app/actions.ts
'use server'

import { z } from 'zod'
import { redirect } from 'next/navigation'

const PostSchema = z.object({
  title: z.string().min(3, 'Title must be at least 3 characters'),
  content: z.string().min(10, 'Content must be at least 10 characters'),
})

export async function createPost(formData: FormData) {
  const rawData = {
    title: formData.get('title'),
    content: formData.get('content'),
  }

  // Validate
  const parsed = PostSchema.safeParse(rawData)

  if (!parsed.success) {
    return {
      errors: parsed.error.flatten().fieldErrors,
    }
  }

  // Mutate
  await db.posts.create(parsed.data)

  // Revalidate and redirect
  revalidateTag('posts', 'max')
  redirect('/posts')
}
```

**See Template**: `templates/server-actions-form.tsx`
**See Reference**: `references/server-actions-guide.md`

---

### 3. Error Handling

**Pattern**: Return error objects from Server Actions, handle in Client Components.

**Server Action**:
```typescript
// app/actions.ts
'use server'

export async function deletePost(id: string) {
  try {
    await db.posts.delete({ where: { id } })
    revalidateTag('posts', 'max')
    return { success: true }
  } catch (error) {
    return {
      success: false,
      error: 'Failed to delete post. Please try again.'
    }
  }
}
```

**Client Component**:
```typescript
'use client'

import { useState } from 'react'
import { deletePost } from '@/app/actions'

export function DeleteButton({ postId }: { postId: string }) {
  const [error, setError] = useState<string | null>(null)

  async function handleDelete() {
    const result = await deletePost(postId)

    if (!result.success) {
      setError(result.error)
    }
  }

  return (
    <div>
      <button onClick={handleDelete}>Delete Post</button>
      {error && <p className="error">{error}</p>}
    </div>
  )
}
```

---

### 4. Optimistic Updates

**Pattern**: Show UI update immediately, then sync with server.

```typescript
'use client'

import { useOptimistic } from 'react'
import { likePost } from '@/app/actions'

export function LikeButton({ postId, initialLikes }: { postId: string; initialLikes: number }) {
  const [optimisticLikes, addOptimisticLike] = useOptimistic(
    initialLikes,
    (state, amount: number) => state + amount
  )

  async function handleLike() {
    // Update UI immediately
    addOptimisticLike(1)

    // Sync with server
    await likePost(postId)
  }

  return (
    <button onClick={handleLike}>
      ❤️ {optimisticLikes} likes
    </button>
  )
}
```

**See Reference**: `references/server-actions-guide.md`

---

## Route Handlers

Route Handlers are server-side API endpoints in the App Router. They replace API Routes from the Pages Router.

### 1. Basic Route Handler

**File**: `app/api/hello/route.ts`

```typescript
import { NextResponse } from 'next/server'

export async function GET() {
  return NextResponse.json({ message: 'Hello, World!' })
}

export async function POST(request: Request) {
  const body = await request.json()

  return NextResponse.json({
    message: 'Post created',
    data: body
  })
}
```

**Supported Methods**: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`

**See Template**: `templates/route-handler-api.ts`

---

### 2. Dynamic Routes

**File**: `app/api/posts/[id]/route.ts`

```typescript
import { NextResponse } from 'next/server'

export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params // ✅ Await params in Next.js 16

  const post = await db.posts.findUnique({ where: { id } })

  if (!post) {
    return NextResponse.json(
      { error: 'Post not found' },
      { status: 404 }
    )
  }

  return NextResponse.json(post)
}

export async function DELETE(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params

  await db.posts.delete({ where: { id } })

  return NextResponse.json({ message: 'Post deleted' })
}
```

---

### 3. Search Params

**URL**: `/api/posts?tag=javascript&limit=10`

```typescript
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const tag = searchParams.get('tag')
  const limit = parseInt(searchParams.get('limit') || '10')

  const posts = await db.posts.findMany({
    where: { tags: { has: tag } },
    take: limit,
  })

  return NextResponse.json(posts)
}
```

---

### 4. Webhooks

**Pattern**: Handle incoming webhooks from third-party services.

```typescript
// app/api/webhooks/stripe/route.ts
import { NextResponse } from 'next/server'
import { headers } from 'next/headers'

export async function POST(request: Request) {
  const body = await request.text()
  const headersList = await headers() // ✅ Await headers in Next.js 16
  const signature = headersList.get('stripe-signature')

  // Verify webhook signature
  const event = stripe.webhooks.constructEvent(
    body,
    signature!,
    process.env.STRIPE_WEBHOOK_SECRET!
  )

  // Handle event
  switch (event.type) {
    case 'payment_intent.succeeded':
      await handlePaymentSuccess(event.data.object)
      break
    case 'payment_intent.failed':
      await handlePaymentFailure(event.data.object)
      break
  }

  return NextResponse.json({ received: true })
}
```

**See Template**: `templates/route-handler-api.ts`
**See Reference**: `references/route-handlers-reference.md`

---

## Proxy vs Middleware

**Next.js 16 introduces `proxy.ts`** to replace `middleware.ts`.

### Why the Change?

- **`middleware.ts`**: Runs on Edge runtime (limited Node.js APIs)
- **`proxy.ts`**: Runs on Node.js runtime (full Node.js APIs)

The new `proxy.ts` makes the network boundary explicit and provides more flexibility.

### Migration

**Before (middleware.ts)**:
```typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Check auth
  const token = request.cookies.get('token')

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: '/dashboard/:path*',
}
```

**After (proxy.ts)**:
```typescript
// proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function proxy(request: NextRequest) {
  // Check auth
  const token = request.cookies.get('token')

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: '/dashboard/:path*',
}
```

**See Template**: `templates/proxy-migration.ts`
**See Reference**: `references/proxy-vs-middleware.md`

---

## Parallel Routes & Route Groups

### 1. Parallel Routes

**Use Case**: Render multiple pages in the same layout (e.g., modal + main content, dashboard panels).

**Structure**:
```
app/
├── @modal/
│   ├── login/
│   │   └── page.tsx
│   └── default.tsx  ← REQUIRED in Next.js 16
├── @feed/
│   ├── trending/
│   │   └── page.tsx
│   └── default.tsx  ← REQUIRED in Next.js 16
└── layout.tsx
```

**Layout**:
```typescript
// app/layout.tsx
export default function Layout({
  children,
  modal,
  feed,
}: {
  children: React.ReactNode
  modal: React.ReactNode
  feed: React.ReactNode
}) {
  return (
    <html>
      <body>
        {modal}
        <main>
          {children}
          <aside>{feed}</aside>
        </main>
      </body>
    </html>
  )
}
```

**Default Files (REQUIRED)**:
```typescript
// app/@modal/default.tsx
export default function ModalDefault() {
  return null
}

// app/@feed/default.tsx
export default function FeedDefault() {
  return <div>Default Feed</div>
}
```

**See Template**: `templates/parallel-routes-with-default.tsx`

---

### 2. Route Groups

**Use Case**: Organize routes without affecting URL structure.

**Structure**:
```
app/
├── (marketing)/
│   ├── about/
│   │   └── page.tsx       → /about
│   └── contact/
│       └── page.tsx       → /contact
├── (shop)/
│   ├── products/
│   │   └── page.tsx       → /products
│   └── cart/
│       └── page.tsx       → /cart
└── layout.tsx
```

**Different Layouts per Group**:
```
app/
├── (marketing)/
│   ├── layout.tsx          ← Marketing layout
│   └── about/page.tsx
├── (shop)/
│   ├── layout.tsx          ← Shop layout
│   └── products/page.tsx
└── layout.tsx              ← Root layout
```

**See Reference**: `references/app-router-fundamentals.md`

---

## React 19.2 Features

Next.js 16 integrates React 19.2, which includes new features from React Canary.

### 1. View Transitions

**Use Case**: Smooth animations between page transitions.

```typescript
'use client'

import { useRouter } from 'next/navigation'
import { startTransition } from 'react'

export function NavigationLink({ href, children }: { href: string; children: React.ReactNode }) {
  const router = useRouter()

  function handleClick(e: React.MouseEvent) {
    e.preventDefault()

    // Wrap navigation in startTransition for View Transitions
    startTransition(() => {
      router.push(href)
    })
  }

  return <a href={href} onClick={handleClick}>{children}</a>
}
```

**With CSS View Transitions API**:
```css
/* app/globals.css */
@view-transition {
  navigation: auto;
}

/* Animate elements with view-transition-name */
.page-title {
  view-transition-name: page-title;
}
```

**See Template**: `templates/view-transitions-react-19.tsx`

---

### 2. `useEffectEvent()` (Experimental)

**Use Case**: Extract non-reactive logic from `useEffect`.

```typescript
'use client'

import { useEffect, experimental_useEffectEvent as useEffectEvent } from 'react'

export function ChatRoom({ roomId }: { roomId: string }) {
  const onConnected = useEffectEvent(() => {
    console.log('Connected to room:', roomId)
  })

  useEffect(() => {
    const connection = connectToRoom(roomId)
    onConnected() // Non-reactive callback

    return () => connection.disconnect()
  }, [roomId]) // Only re-run when roomId changes

  return <div>Chat Room {roomId}</div>
}
```

**Why Use It**: Prevents unnecessary `useEffect` re-runs when callback dependencies change.

---

### 3. React Compiler (Stable)

**Use Case**: Automatic memoization without `useMemo`, `useCallback`.

**Enable in next.config.ts**:
```typescript
import type { NextConfig } from 'next'

const config: NextConfig = {
  experimental: {
    reactCompiler: true,
  },
}

export default config
```

**Install Plugin**:
```bash
npm install babel-plugin-react-compiler
```

**Example** (no manual memoization needed):
```typescript
'use client'

export function ExpensiveList({ items }: { items: string[] }) {
  // React Compiler automatically memoizes this
  const filteredItems = items.filter(item => item.length > 3)

  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  )
}
```

**See Reference**: `references/react-19-integration.md`

---

## Metadata API

The Metadata API provides type-safe SEO and social sharing metadata.

### 1. Static Metadata

**Pattern**: Export `metadata` object from page or layout.

```typescript
// app/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'My App',
  description: 'Welcome to my app',
  openGraph: {
    title: 'My App',
    description: 'Welcome to my app',
    images: ['/og-image.jpg'],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'My App',
    description: 'Welcome to my app',
    images: ['/twitter-image.jpg'],
  },
}

export default function Page() {
  return <h1>Home</h1>
}
```

---

### 2. Dynamic Metadata

**Pattern**: Export `generateMetadata` async function.

```typescript
// app/posts/[id]/page.tsx
import type { Metadata } from 'next'

export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
  const { id } = await params
  const post = await fetch(`/api/posts/${id}`).then(r => r.json())

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage],
    },
  }
}

export default async function PostPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  const post = await fetch(`/api/posts/${id}`).then(r => r.json())

  return <article>{post.content}</article>
}
```

---

### 3. Sitemap

**File**: `app/sitemap.ts`

```typescript
import type { MetadataRoute } from 'next'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await fetch('/api/posts').then(r => r.json())

  const postUrls = posts.map((post: { id: string; updatedAt: string }) => ({
    url: `https://example.com/posts/${post.id}`,
    lastModified: post.updatedAt,
    changeFrequency: 'weekly' as const,
    priority: 0.8,
  }))

  return [
    {
      url: 'https://example.com',
      lastModified: new Date(),
      changeFrequency: 'daily',
      priority: 1,
    },
    ...postUrls,
  ]
}
```

**See Template**: `templates/metadata-config.ts`
**See Reference**: `references/metadata-api-guide.md`

---

## Image & Font Optimization

### 1. next/image

**Basic Usage**:
```typescript
import Image from 'next/image'

export function MyImage() {
  return (
    <Image
      src="/hero.jpg"
      alt="Hero image"
      width={1200}
      height={600}
      priority // Load above the fold
    />
  )
}
```

**Responsive Images**:
```typescript
<Image
  src="/hero.jpg"
  alt="Hero"
  fill
  style={{ objectFit: 'cover' }}
  sizes="(max-width: 768px) 100vw, 50vw"
/>
```

**Remote Images** (configure in next.config.ts):
```typescript
import type { NextConfig } from 'next'

const config: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.example.com',
        pathname: '/images/**',
      },
    ],
  },
}

export default config
```

**See Template**: `templates/image-optimization.tsx`

---

### 2. next/font

**Google Fonts**:
```typescript
// app/layout.tsx
import { Inter, Roboto_Mono } from 'next/font/google'



…(truncated)
