- name
- nextjs
- description
- Next.js App Router expert guidance. Use when building, debugging, or architecting Next.js applications — routing, Server Components, Server Actions, Cache Components, layouts, middleware/proxy, data fetching, rendering strategies, and deployment on Vercel.
- metadata
- {"priority":5,"docs":["https://nextjs.org/docs","https://nextjs.org/docs/app"],"sitemap":"https://nextjs.org/sitemap.xml","pathPatterns":["next.config.*","next-env.d.ts","app/**","pages/**","src/app/**","src/pages/**","tailwind.config.*","postcss.config.*","tsconfig.json","tsconfig.*.json","apps/*/app/**","apps/*/pages/**","apps/*/src/app/**","apps/*/src/pages/**","apps/*/next.config.*"],"bashPatterns":["\\bnext\\s+(dev|build|start|lint)\\b","\\bnext\\s+experimental-analyze\\b","\\bnpx\\s+create-next-app\\b","\\bbunx\\s+create-next-app\\b","\\bnpm\\s+run\\s+(dev|build|start)\\b","\\bpnpm\\s+(dev|build)\\b","\\bbun\\s+run\\s+(dev|build)\\b"],"promptSignals":{"phrases":["next.js","nextjs","app router","server component","server action"],"allOf":["[Truncated]","[Truncated]"],"anyOf":["pages router","getserversideprops","use server"],"noneOf":[],"minScore":6}}
# Next.js (v16+) — App Router
You are an expert in Next.js 16 with the App Router. Always prefer the App Router over the legacy Pages Router unless the user's project explicitly uses Pages Router.
## Critical Pattern: Lazy Initialization for Build-Safe Modules
Never initialize database clients (Neon, Drizzle), Redis (Upstash), or service SDKs (Resend, Slack) at module scope.
During `next build`, static generation can evaluate modules before runtime env vars are present, which causes startup crashes. Always initialize these clients lazily inside getter functions.
```ts
import { drizzle } from 'drizzle-orm/neon-http'
import { neon } from '@neondatabase/serverless'
let _db: ReturnType<typeof drizzle> | null = null
export function getDb() {
if (!_db) _db = drizzle(neon(process.env.DATABASE_URL!))
return _db
}
```
Apply the same lazy singleton pattern to Redis and SDK clients (`getRedis()`, `getResend()`, `getSlackClient()`) instead of creating them at import time.
## Scaffolding
When running `create-next-app`, **always** pass `--yes` to skip interactive prompts (React Compiler, import alias) that hang in non-interactive shells:
```bash
npx create-next-app@latest my-app --yes --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --turbopack --use-npm
```
**If the target directory contains ANY non-Next.js files** (`.git/`, config files, etc.), you **MUST** add `--force`. Without it, `create-next-app` will fail with "The directory contains files that could conflict" and block scaffolding. **Check the directory first** — if it has anything in it, use `--force`:
```bash
npx create-next-app@latest . --yes --force --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --turbopack --use-npm
```
### Geist Font Fix (Tailwind v4 + shadcn)
`shadcn init` rewrites `globals.css` with `--font-sans: var(--font-sans)` in `@theme inline` — a circular reference that falls back to Times/serif. Even `var(--font-geist-sans)` doesn't work because Tailwind v4's `@theme inline` resolves at **CSS parse time**, not runtime.
**Two fixes required after `create-next-app` + `shadcn init`:**
1. **Use literal font names in `globals.css`** (not CSS variable references):
```css
@theme inline {
/* CORRECT — literal names that @theme can resolve at parse time */
--font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
--font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;
}
```
2. **Move font variable classNames from `<body>` to `<html>`** in layout.tsx:
```tsx
// app/layout.tsx — CORRECT
<html lang="en" className={`${geistSans.variable} ${geistMono.variable}`}>
<body className="antialiased">
```
```tsx
// app/layout.tsx — WRONG (default scaffold output)
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
```
**Always apply both fixes** after running `create-next-app` + `shadcn init` with Tailwind v4.
## UI Defaults for App Router Pages
When building pages, layouts, and route-level UI in this stack, default to shadcn/ui + Geist instead of raw Tailwind scaffolding.
- Start from theme tokens: `bg-background text-foreground`, not ad-hoc palette classes.
- Use Geist Sans for interface text and Geist Mono for code, metrics, IDs, timestamps.
- Reach for shadcn primitives first: Button, Input, Textarea, Card, Tabs, Table, Dialog, AlertDialog, Sheet, DropdownMenu, Badge, Separator, Skeleton.
- For product, admin, and AI surfaces, default to dark mode with restrained accents and designed empty/loading/error states.
- Common route compositions: Settings route (Tabs+Card+form), Dashboard route (filter bar+Card+Table), Mobile nav (Sheet+Button).
## Key Architecture
Next.js 16 uses React 19.2 features and the App Router (file-system routing under `app/`). Ensure React **19.2.4+** for security patches (see CVE section below).
### File Conventions
- `layout.tsx` — Persistent wrapper, preserves state across navigations
- `page.tsx` — Unique UI for a route, makes route publicly accessible
- `loading.tsx` — Suspense fallback shown while segment loads
- `error.tsx` — Error boundary for a segment
- `not-found.tsx` — 404 UI for a segment
- `route.ts` — API endpoint (Route Handler)
- `template.tsx` — Like layout but re-mounts on navigation
- `default.tsx` — Fallback for parallel routes
### Routing
- Dynamic segments: `[id]`, catch-all: `[...slug]`, optional catch-all: `[[...slug]]`
- Route groups: `(group)` — organize without affecting URL
- Parallel routes: `@slot` — render multiple pages in same layout
- Intercepting routes: `(.)`, `(..)`, `(...)`, `(..)(..)` — modal patterns
## Server Components (Default)
All components in the App Router are Server Components by default. They:
- Run on the server only, ship zero JavaScript to the client
- Can directly `await` data (fetch, DB queries, file system)
- Cannot use `useState`, `useEffect`, or browser APIs
- Cannot use event handlers (`onClick`, `onChange`)
```tsx
// app/users/page.tsx — Server Component (default)
export default async function UsersPage() {
const users = await db.query('SELECT * FROM users')
return <UserList users={users} />
}
```
## Client Components
Add `'use client'` at the top of the file when you need interactivity or browser APIs.
```tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```
**Rule**: Push `'use client'` as far down the component tree as possible. Keep data fetching in Server Components and pass data down as props.
## Server Actions / Server Functions
Async functions marked with `'use server'` that run on the server. Use for mutations.
```tsx
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
const name = formData.get('name') as string
await db.insert('users', { name })
revalidatePath('/users')
}
```
Use Server Actions for:
- Form submissions and data mutations
- In-app mutations with `revalidatePath` / `revalidateTag`
Use Route Handlers (`route.ts`) for:
- Public APIs consumed by external clients
- Webhooks
- Large file uploads
- Streaming responses
## Cache Components (Next.js 16)
The `'use cache'` directive enables component and function-level caching.
```tsx
'use cache'
export async function CachedUserList() {
cacheLife('hours') // Configure cache duration
cacheTag('users') // Tag for on-demand invalidation
const users = await db.query('SELECT * FROM users')
return <UserList users={users} />
}
```
### Cache Scope Variants
`'use cache'` supports scope modifiers that control where cached data is stored:
```tsx
// Default — cached in the deployment's local data cache
'use cache'
// Remote cache — shared across all deployments and regions (Vercel only)
'use cache: remote'
// Private cache — per-request cache, never shared between users
'use cache: private'
```
| Variant | Shared across deployments? | Shared across users? | Use case |
|---------|---------------------------|---------------------|----------|
| `'use cache'` | No (per-deployment) | Yes | Default, most use cases |
| `'use cache: remote'` | Yes | Yes | Expensive computations shared globally |
| `'use cache: private'` | No | No | User-specific cached data (e.g., profile) |
### Cache Handler Configuration
Next.js 16 uses `cacheHandlers` (plural) to configure separate handlers for different cache types:
```ts
// next.config.ts
const nextConfig = {
cacheHandlers: {
default: require.resolve('./cache-handler-default.mjs'),
remote: require.resolve('./cache-handler-remote.mjs'),
fetch: require.resolve('./cache-handler-fetch.mjs'),
},
}
```
**Important**: `cacheHandlers` (plural, Next.js 16+) replaces `cacheHandler` (singular, Next.js 15). The singular form configured one handler for all cache types. The plural form allows per-type handlers (`default`, `remote`, `fetch`). Using the old singular `cacheHandler` in Next.js 16 triggers a deprecation warning.
### Cache Invalidation
Invalidate with `updateTag('users')` from a Server Action (immediate expiration, Server Actions only) or `revalidateTag('users', 'max')` for stale-while-revalidate from Server Actions or Route Handlers.
**Important**: The single-argument `revalidateTag(tag)` is deprecated in Next.js 16. Always pass a `cacheLife` profile as the second argument (e.g., `'max'`, `'hours'`, `'days'`).
| Function | Context | Behavior |
|----------|---------|----------|
| `updateTag(tag)` | Server Actions only | Immediate expiration, read-your-own-writes |
| `revalidateTag(tag, 'max')` | Server Actions + Route Handlers | Stale-while-revalidate (recommended) |
| `revalidateTag(tag, { expire: 0 })` | Route Handlers (webhooks) | Immediate expiration from external triggers |
## Proxy (formerly Middleware)
In Next.js 16, `middleware.ts` is renamed to `proxy.ts`. It runs **exclusively on the Node.js runtime** — the Edge runtime is not supported in proxy and cannot be configured.
**File location**: Place `proxy.ts` at the same level as your `app/` directory:
- Standard project: `proxy.ts` at project root
- With `--src-dir` (or `srcDir: true`): `src/proxy.ts` (inside `src/`, alongside `app/`)
If you place `proxy.ts` in the wrong location, Next.js will silently ignore it and no request interception will occur.
**Constraints**:
- Proxy can only **rewrite**, **redirect**, or **modify headers** — it cannot return full response bodies. Use Route Handlers for that.
- Config flags are renamed: `skipMiddlewareUrlNormalize` → `skipProxyUrlNormalize`.
- Keep it light: use for high-level traffic control (e.g., redirecting users without a session cookie). Detailed auth should live in Server Components or Server Actions.
- **OpenNext note**: OpenNext doesn't support `proxy.ts` yet — keep using `middleware.ts` if self-hosting with OpenNext.
```ts
// proxy.ts (or src/proxy.ts if using src directory)
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Rewrite, redirect, set headers, etc.
}
export const config = { matcher: ['/dashboard/:path*'] }
```
## Upgrading
Use the built-in upgrade command (available since 16.1.0):
```bash
pnpm next upgrade # or npm/yarn/bun equivalent
```
For versions before 16.1.0: `npx @next/codemod@canary upgrade latest`
If your AI coding assistant supports MCP, the **Next.js DevTools MCP** can automate upgrade and migration tasks.
## What's New in Next.js 16.1
Next.js 16.1 (December 2025, latest stable patch: 16.1.6) builds on 16.0 with developer experience improvements:
1. **Turbopack File System Caching (Stable)** — Compiler artifacts are now cached on disk between `next dev` restarts, delivering up to 14× faster startup on large projects. Enabled by default, no config needed. File system caching for `next build` is planned next.
2. **Bundle Analyzer (Experimental)** — New built-in bundle analyzer works with Turbopack. Offers route-specific filtering, import tracing, and RSC boundary analysis to identify bloated dependencies in both server and client bundles. Enable with `experimental.bundleAnalyzer: true` in `next.config`.
3. **`next dev --inspect`** — Debug your dev server without global `NODE_OPTIONS=--inspect`. Applies the inspector only to the relevant process.
4. **`next upgrade` command** — New CLI command to simplify version upgrades: `npx @next/codemod@canary upgrade latest`.
5. **Transitive External Dependencies** — Turbopack correctly resolves and externalizes transitive deps in `serverExternalPackages` without extra config.
6. **20 MB smaller installs** — Streamlined Turbopack caching layer reduces `node_modules` footprint.
## React 19.2 Features
Next.js 16+ uses React 19.2. These features are available in App Router applications:
### `useEffectEvent` Hook
Creates a stable function that always accesses the latest props and state without triggering effect re-runs. Use when your effect needs to read a value but shouldn't re-run when that value changes:
```tsx
'use client'
import { useEffect, useEffectEvent } from 'react'
function ChatRoom({ roomId, theme }: { roomId: string; theme: string }) {
const onConnected = useEffectEvent(() => {
showNotification(`Connected to ${roomId}`, theme) // reads latest theme
})
useEffect(() => {
const connection = createConnection(roomId)
connection.on('connected', onConnected)
connection.connect()
return () => connection.disconnect()
}, [roomId]) // theme is NOT a dependency — onConnected reads it via useEffectEvent
}
```
Common use cases: logging with current state, notifications using current theme, callbacks that need fresh values but aren't the trigger for the effect.
### `<Activity>` Component
Preserves component state when hiding and showing UI, without unmounting. Solves the classic tradeoff between unmounting (loses state) and CSS `display:none` (effects keep running):
```tsx
'use client'
import { Activity, useState } from 'react'
function TabContainer() {
const [activeTab, setActiveTab] = useState('inbox')
return (
<div>
<nav>
Ver en GitHub