nextjs
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNext.js (v16+) — App Router
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.
您是Next.js 16 App Router领域的专家。除非用户项目明确使用Pages Router,否则始终优先使用App Router而非传统的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 , static generation can evaluate modules before runtime env vars are present, which causes startup crashes. Always initialize these clients lazily inside getter functions.
next buildts
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 (, , ) instead of creating them at import time.
getRedis()getResend()getSlackClient()切勿在模块作用域初始化数据库客户端(Neon、Drizzle)、Redis(Upstash)或服务SDK(Resend、Slack)。
在执行时,静态生成会在运行时环境变量加载前解析模块,这会导致启动崩溃。请始终在 getter 函数内部延迟初始化这些客户端。
next buildts
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
}对Redis和SDK客户端(、、)应用相同的延迟单例模式,而非在导入时创建它们。
getRedis()getResend()getSlackClient()Scaffolding
项目脚手架
When running , always pass to skip interactive prompts (React Compiler, import alias) that hang in non-interactive shells:
create-next-app--yesbash
npx create-next-app@latest my-app --yes --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --turbopack --use-npmIf the target directory contains ANY non-Next.js files (, , , config files, etc.), you MUST add . Without it, will fail with "The directory contains files that could conflict" and block scaffolding. Check the directory first — if it has anything in it, use :
.claude/CLAUDE.md.git/--forcecreate-next-app--forcebash
npx create-next-app@latest . --yes --force --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --turbopack --use-npm运行时,务必传入以跳过交互式提示(React Compiler、导入别名),这些提示在非交互式shell中会导致进程挂起:
create-next-app--yesbash
npx create-next-app@latest my-app --yes --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --turbopack --use-npm如果目标目录包含任何非Next.js文件(、、、配置文件等),您必须添加。如果不添加,会因“目录中存在可能冲突的文件”而失败并阻止脚手架创建。请先检查目录——如果目录中有任何内容,请使用:
.claude/CLAUDE.md.git/--forcecreate-next-app--forcebash
npx create-next-app@latest . --yes --force --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --turbopack --use-npmGeist Font Fix (Tailwind v4 + shadcn)
Geist字体修复(Tailwind v4 + shadcn)
shadcn initglobals.css--font-sans: var(--font-sans)@theme inlinevar(--font-geist-sans)@theme inlineTwo fixes required after + :
create-next-appshadcn init- Use literal font names in (not CSS variable references):
globals.css
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;
}- Move font variable classNames from to
<body>in layout.tsx:<html>
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 + with Tailwind v4.
create-next-appshadcn initshadcn initglobals.css@theme inline--font-sans: var(--font-sans)var(--font-geist-sans)@theme inline在 + 后需要两个修复:
create-next-appshadcn init- 在中使用字面字体名称(而非CSS变量引用):
globals.css
css
@theme inline {
/* 正确写法 —— @theme可在解析阶段识别的字面名称 */
--font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
--font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;
}- 将字体变量类名从移至
<body>(在layout.tsx中):<html>
tsx
// app/layout.tsx —— 正确写法
<html lang="en" className={`${geistSans.variable} ${geistMono.variable}`}>
<body className="antialiased">tsx
// app/layout.tsx —— 错误写法(默认脚手架输出)
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>在使用Tailwind v4执行 + 后,请务必同时应用这两个修复。
create-next-appshadcn initUI Defaults for App Router Pages
App Router页面的UI默认规范
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: , not ad-hoc palette classes.
bg-background text-foreground - 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).
在此技术栈中构建页面、布局和路由级UI时,默认使用shadcn/ui + Geist而非原生Tailwind脚手架。
- 从主题令牌开始:使用,而非临时调色板类。
bg-background text-foreground - 界面文本使用Geist Sans,代码、指标、ID、时间戳使用Geist Mono。
- 优先使用shadcn原语:Button、Input、Textarea、Card、Tabs、Table、Dialog、AlertDialog、Sheet、DropdownMenu、Badge、Separator、Skeleton。
- 对于产品、管理和AI界面,默认使用深色模式,搭配克制的强调色,并设计空状态、加载状态和错误状态。
- 常见路由组合:设置路由(Tabs+Card+表单)、仪表板路由(筛选栏+Card+Table)、移动端导航(Sheet+Button)。
Key Architecture
核心架构
Next.js 16 uses React 19.2 features and the App Router (file-system routing under ). Ensure React 19.2.4+ for security patches (see CVE section below).
app/Next.js 16使用React 19.2特性和App Router(目录下的文件系统路由)。请确保使用React **19.2.4+**以获取安全补丁(参见下方CVE部分)。
app/File Conventions
文件约定
- — Persistent wrapper, preserves state across navigations
layout.tsx - — Unique UI for a route, makes route publicly accessible
page.tsx - — Suspense fallback shown while segment loads
loading.tsx - — Error boundary for a segment
error.tsx - — 404 UI for a segment
not-found.tsx - — API endpoint (Route Handler)
route.ts - — Like layout but re-mounts on navigation
template.tsx - — Fallback for parallel routes
default.tsx
- —— 持久化包装器,在导航间保留状态
layout.tsx - —— 路由的唯一UI,使路由可公开访问
page.tsx - —— 片段加载时显示的Suspense回退UI
loading.tsx - —— 片段的错误边界
error.tsx - —— 片段的404 UI
not-found.tsx - —— API端点(Route Handler)
route.ts - —— 类似布局,但在导航时重新挂载
template.tsx - —— 并行路由的回退UI
default.tsx
Routing
路由
- Dynamic segments: , catch-all:
[id], optional catch-all:[...slug][[...slug]] - Route groups: — organize without affecting URL
(group) - Parallel routes: — render multiple pages in same layout
@slot - Intercepting routes: ,
(.),(..),(...)— modal patterns(..)(..)
- 动态片段:,捕获所有片段:
[id],可选捕获所有片段:[...slug][[...slug]] - 路由组:—— 组织路由但不影响URL
(group) - 并行路由:—— 在同一布局中渲染多个页面
@slot - 拦截路由:、
(.)、(..)、(...)—— 模态框模式(..)(..)
Server Components (Default)
Server Components(默认)
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 data (fetch, DB queries, file system)
await - Cannot use ,
useState, or browser APIsuseEffect - 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} />
}App Router中的所有组件默认都是Server Components。它们:
- 仅在服务器端运行,不会向客户端发送任何JavaScript
- 可直接数据(fetch、数据库查询、文件系统)
await - 无法使用、
useState或浏览器APIuseEffect - 无法使用事件处理程序(、
onClick)onChange
tsx
// app/users/page.tsx —— Server Component(默认)
export default async function UsersPage() {
const users = await db.query('SELECT * FROM users')
return <UserList users={users} />
}Client Components
Client Components
Add at the top of the file when you need interactivity or browser APIs.
'use client'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 as far down the component tree as possible. Keep data fetching in Server Components and pass data down as props.
'use client'当需要交互性或浏览器API时,在文件顶部添加。
'use client'tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}规则:将尽可能向下推到组件树中。将数据获取保留在Server Components中,并通过props传递数据。
'use client'Server Actions / Server Functions
Server Actions / Server Functions
Async functions marked with that run on the server. Use for mutations.
'use server'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 /
revalidatePathrevalidateTag
Use Route Handlers () for:
route.ts- Public APIs consumed by external clients
- Webhooks
- Large file uploads
- Streaming responses
标记有的异步函数,在服务器端运行。用于数据变更操作。
'use server'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')
}Server Actions适用于:
- 表单提交和数据变更
- 使用/
revalidatePath的应用内数据变更revalidateTag
Route Handlers()适用于:
route.ts- 供外部客户端使用的公共API
- Webhook
- 大文件上传
- 流式响应
Cache Components (Next.js 16)
Cache Components(Next.js 16)
The directive enables component and function-level caching.
'use cache'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} />
}'use cache'tsx
'use cache'
export async function CachedUserList() {
cacheLife('hours') // 配置缓存时长
cacheTag('users') // 用于按需失效的标签
const users = await db.query('SELECT * FROM users')
return <UserList users={users} />
}Cache Scope Variants
缓存范围变体
'use cache'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 |
|---|---|---|---|
| No (per-deployment) | Yes | Default, most use cases |
| Yes | Yes | Expensive computations shared globally |
| No | No | User-specific cached data (e.g., profile) |
'use cache'tsx
// 默认 —— 缓存在部署的本地数据缓存中
'use cache'
// 远程缓存 —— 在所有部署和区域间共享(仅Vercel支持)
'use cache: remote'
// 私有缓存 —— 每个请求独立缓存,不会在用户间共享
'use cache: private'| 变体 | 是否在部署间共享 | 是否在用户间共享 | 使用场景 |
|---|---|---|---|
| 否(每个部署独立) | 是 | 默认值,大多数使用场景 |
| 是 | 是 | 需全局共享的昂贵计算 |
| 否 | 否 | 用户特定的缓存数据(如个人资料) |
Cache Handler Configuration
缓存处理器配置
Next.js 16 uses (plural) to configure separate handlers for different cache types:
cacheHandlersts
// 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: (plural, Next.js 16+) replaces (singular, Next.js 15). The singular form configured one handler for all cache types. The plural form allows per-type handlers (, , ). Using the old singular in Next.js 16 triggers a deprecation warning.
cacheHandlerscacheHandlerdefaultremotefetchcacheHandlerNext.js 16使用(复数形式)为不同缓存类型配置独立的处理器:
cacheHandlersts
// 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'),
},
}重要提示:(复数形式,Next.js 16+)替代了(单数形式,Next.js 15)。单数形式为所有缓存类型配置一个处理器,复数形式允许为不同类型配置独立处理器(、、)。在Next.js 16中使用旧的单数会触发弃用警告。
cacheHandlerscacheHandlerdefaultremotefetchcacheHandlerCache Invalidation
缓存失效
Invalidate with from a Server Action (immediate expiration, Server Actions only) or for stale-while-revalidate from Server Actions or Route Handlers.
updateTag('users')revalidateTag('users', 'max')Important: The single-argument is deprecated in Next.js 16. Always pass a profile as the second argument (e.g., , , ).
revalidateTag(tag)cacheLife'max''hours''days'| Function | Context | Behavior |
|---|---|---|
| Server Actions only | Immediate expiration, read-your-own-writes |
| Server Actions + Route Handlers | Stale-while-revalidate (recommended) |
| Route Handlers (webhooks) | Immediate expiration from external triggers |
在Server Action中使用(立即过期,仅Server Actions支持),或在Server Actions或Route Handlers中使用实现 stale-while-revalidate 策略。
updateTag('users')revalidateTag('users', 'max')重要提示:单参数在Next.js 16中已弃用。请始终传递配置文件作为第二个参数(例如、、)。
revalidateTag(tag)cacheLife'max''hours''days'| 函数 | 上下文 | 行为 |
|---|---|---|
| 仅Server Actions | 立即过期,读己写(read-your-own-writes) |
| Server Actions + Route Handlers | Stale-while-revalidate(推荐) |
| Route Handlers(webhook) | 从外部触发立即过期 |
Proxy (formerly Middleware)
Proxy(原Middleware)
In Next.js 16, is renamed to . It runs exclusively on the Node.js runtime — the Edge runtime is not supported in proxy and cannot be configured.
middleware.tsproxy.tsFile location: Place at the same level as your directory:
proxy.tsapp/- Standard project: at project root
proxy.ts - With (or
--src-dir):srcDir: true(insidesrc/proxy.ts, alongsidesrc/)app/
If you place in the wrong location, Next.js will silently ignore it and no request interception will occur.
proxy.tsConstraints:
- 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 yet — keep using
proxy.tsif self-hosting with OpenNext.middleware.ts
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*'] }在Next.js 16中,更名为。它仅在Node.js运行时运行——Edge运行时不支持proxy,无法配置。
middleware.tsproxy.ts文件位置:将放在与目录同级的位置:
proxy.tsapp/- 标准项目:在项目根目录
proxy.ts - 使用(或
--src-dir):srcDir: true(在src/proxy.ts目录内,与src/同级)app/
如果将放在错误位置,Next.js会静默忽略它,不会进行请求拦截。
proxy.ts限制:
- Proxy仅能重写、重定向或修改请求头——无法返回完整响应体。此类场景请使用Route Handlers。
- 配置标志已重命名:→
skipMiddlewareUrlNormalize。skipProxyUrlNormalize - 保持轻量化:用于高级流量控制(例如重定向无会话Cookie的用户)。详细的认证逻辑应放在Server Components或Server Actions中。
- OpenNext注意事项:OpenNext目前不支持——如果使用OpenNext自托管,请继续使用
proxy.ts。middleware.ts
ts
// proxy.ts(若使用src目录则为src/proxy.ts)
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// 重写、重定向、设置请求头等
}
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 equivalentFor versions before 16.1.0:
npx @next/codemod@canary upgrade latestIf your AI coding assistant supports MCP, the Next.js DevTools MCP can automate upgrade and migration tasks.
使用内置的升级命令(从16.1.0开始可用):
bash
pnpm next upgrade # 或使用npm/yarn/bun的等效命令对于16.1.0之前的版本:
npx @next/codemod@canary upgrade latest如果您的AI编码助手支持MCP,Next.js DevTools MCP可自动完成升级和迁移任务。
What's New in Next.js 16.1
Next.js 16.1中的新特性
Next.js 16.1 (December 2025, latest stable patch: 16.1.6) builds on 16.0 with developer experience improvements:
- Turbopack File System Caching (Stable) — Compiler artifacts are now cached on disk between restarts, delivering up to 14× faster startup on large projects. Enabled by default, no config needed. File system caching for
next devis planned next.next build - 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 in
experimental.bundleAnalyzer: true.next.config - — Debug your dev server without global
next dev --inspect. Applies the inspector only to the relevant process.NODE_OPTIONS=--inspect - command — New CLI command to simplify version upgrades:
next upgrade.npx @next/codemod@canary upgrade latest - Transitive External Dependencies — Turbopack correctly resolves and externalizes transitive deps in without extra config.
serverExternalPackages - 20 MB smaller installs — Streamlined Turbopack caching layer reduces footprint.
node_modules
Next.js 16.1(2025年12月,最新稳定补丁:16.1.6)基于16.0版本,提升了开发者体验:
- Turbopack文件系统缓存(稳定版) —— 编译器产物现在会在重启之间缓存到磁盘,大型项目的启动速度最高可提升14倍。默认启用,无需配置。
next dev的文件系统缓存计划后续推出。next build - Bundle Analyzer(实验性) —— 新的内置Bundle Analyzer与Turbopack兼容。提供路由特定过滤、导入追踪和RSC边界分析,可识别服务器和客户端包中的臃肿依赖。在中启用
next.config即可使用。experimental.bundleAnalyzer: true - —— 无需全局设置
next dev --inspect即可调试开发服务器。仅对相关进程应用调试器。NODE_OPTIONS=--inspect - 命令 —— 新的CLI命令简化版本升级:
next upgrade。npx @next/codemod@canary upgrade latest - 传递外部依赖 —— Turbopack可正确解析并外部化中的传递依赖,无需额外配置。
serverExternalPackages - 安装包体积减小20 MB —— 精简的Turbopack缓存层减少了的占用空间。
node_modules
React 19.2 Features
React 19.2特性
Next.js 16+ uses React 19.2. These features are available in App Router applications:
Next.js 16+使用React 19.2。这些特性在App Router应用中可用:
useEffectEvent
Hook
useEffectEventuseEffectEvent
Hook
useEffectEventCreates 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.
创建一个稳定的函数,始终访问最新的props和state,而不会触发effect重新运行。当您的effect需要读取某个值但不应在该值变化时重新运行时使用:
tsx
'use client'
import { useEffect, useEffectEvent } from 'react'
function ChatRoom({ roomId, theme }: { roomId: string; theme: string }) {
const onConnected = useEffectEvent(() => {
showNotification(`Connected to ${roomId}`, theme) // 读取最新的theme
})
useEffect(() => {
const connection = createConnection(roomId)
connection.on('connected', onConnected)
connection.connect()
return () => connection.disconnect()
}, [roomId]) // theme不是依赖项 —— onConnected通过useEffectEvent读取它
}常见使用场景:使用当前状态记录日志、使用当前主题显示通知、需要获取最新值但不是effect触发条件的回调函数。
<Activity>
Component
<Activity><Activity>
组件
<Activity>Preserves component state when hiding and showing UI, without unmounting. Solves the classic tradeoff between unmounting (loses state) and CSS (effects keep running):
display:nonetsx
'use client'
import { Activity, useState } from 'react'
function TabContainer() {
const [activeTab, setActiveTab] = useState('inbox')
return (
<div>
<nav>
<button onClick={() => setActiveTab('inbox')}>Inbox</button>
<button onClick={() => setActiveTab('drafts')}>Drafts</button>
</nav>
<Activity mode={activeTab === 'inbox' ? 'visible' : 'hidden'}>
<InboxPanel />
</Activity>
<Activity mode={activeTab === 'drafts' ? 'visible' : 'hidden'}>
<DraftsPanel />
</Activity>
</div>
)
}Use for: tabbed interfaces, modals, sidebars, background tasks — anywhere you need to maintain component state without keeping everything actively rendered. When , effects are suspended (not running in the background).
mode="hidden"在隐藏和显示UI时保留组件状态,无需卸载。解决了经典的两难问题:卸载会丢失状态,而CSS 会让effect继续运行:
display:nonetsx
'use client'
import { Activity, useState } from 'react'
function TabContainer() {
const [activeTab, setActiveTab] = useState('inbox')
return (
<div>
<nav>
<button onClick={() => setActiveTab('inbox')}>收件箱</button>
<button onClick={() => setActiveTab('drafts')}>草稿</button>
</nav>
<Activity mode={activeTab === 'inbox' ? 'visible' : 'hidden'}>
<InboxPanel />
</Activity>
<Activity mode={activeTab === 'drafts' ? 'visible' : 'hidden'}>
<DraftsPanel />
</Activity>
</div>
)
}适用于:标签页界面、模态框、侧边栏、后台任务——任何需要保持组件状态而无需始终渲染的场景。当时,effect会被暂停(不会在后台运行)。
mode="hidden"View Transitions API
视图过渡API
React 19.2 supports the browser View Transitions API for animating elements across navigations. Next.js 16 has built-in support — elements can animate between route changes without manual transition logic.
Key change: now generates IDs with prefix (instead of ) to be valid for and XML 1.0 names.
useId_r_:r:view-transition-nameReact 19.2支持浏览器的View Transitions API,用于在导航间为元素添加动画。Next.js 16内置支持——元素可在路由变化时添加动画,无需手动编写过渡逻辑。
关键变化:现在生成带有前缀的ID(而非),以符合和XML 1.0名称的要求。
useId_r_:r:view-transition-nameLayout Deduplication During Prefetching
预取期间的布局去重
Next.js 16 deduplicates shared layouts during prefetching. When multiple components point to routes with a shared layout, the layout is downloaded once instead of separately for each link.
<Link>Impact: A page with 50 product links that share a layout downloads ~198KB instead of ~2.4MB — a 92% reduction in prefetch network transfer.
Combined with incremental prefetching, Next.js only fetches route segments not already in cache, cancels prefetch requests when links leave the viewport, re-prefetches on hover or viewport re-entry, and re-prefetches when data is invalidated.
Next.js 16在预取期间对共享布局进行去重。当多个组件指向具有共享布局的路由时,布局只会被下载一次,而非为每个链接单独下载。
<Link>影响:一个包含50个产品链接且共享布局的页面,预取时的网络传输量从约2.4MB减少到约198KB——减少了92%。
结合增量预取,Next.js仅获取缓存中没有的路由片段,当链接离开视口时取消预取请求,在悬停或重新进入视口时重新预取,在数据失效时重新预取。
Bundle Analyzer (next experimental-analyze
)
next experimental-analyzeBundle Analyzer(next experimental-analyze
)
next experimental-analyzeBuilt-in bundle analyzer that works with Turbopack (available since Next.js 16.1):
bash
undefined与Turbopack兼容的内置Bundle Analyzer(从Next.js 16.1开始可用):
bash
undefinedAnalyze and serve results in browser
分析并在浏览器中展示结果
next experimental-analyze --serve
next experimental-analyze --serve
Analyze with custom port
使用自定义端口分析
next experimental-analyze --serve --port 4001
next experimental-analyze --serve --port 4001
Write analysis to .next/diagnostics/analyze (no server)
将分析结果写入.next/diagnostics/analyze(不启动服务器)
next experimental-analyze
Features:
- Route-specific filtering between client and server bundles
- Full import chain tracing — see exactly why a module is included
- Traces imports across RSC boundaries and dynamic imports
- No application build required — analyzes module graph directly
Save output for comparison: `cp -r .next/diagnostics/analyze ./analyze-before-refactor`
**Legacy**: For projects not using Turbopack, use `@next/bundle-analyzer` with `ANALYZE=true npm run build`.next experimental-analyze
特性:
- 客户端和服务器包的路由特定过滤
- 完整的导入链追踪——准确查看模块被包含的原因
- 跨RSC边界和动态导入的导入追踪
- 无需构建应用——直接分析模块图
保存输出用于对比:`cp -r .next/diagnostics/analyze ./analyze-before-refactor`
**旧版方案**:对于未使用Turbopack的项目,使用`@next/bundle-analyzer`并执行`ANALYZE=true npm run build`。Next.js 16.2 (Canary)
Next.js 16.2(Canary版)
Next.js 16.2 is currently in canary (latest: 16.2.0-canary.84, March 2026). Key areas in development:
- Turbopack File System Caching for — Extending the stable
next buildFS cache to production builds for faster CI.next dev - Proxy refinements — Continued iteration on (the Node.js-runtime replacement for
proxy.tsintroduced in 16.0). The proxy API is stabilizing with improved request context and streaming support.middleware.ts - React Compiler optimizations — Further automatic memoization improvements building on the stable React Compiler in 16.0.
Note: Canary releases are not recommended for production. Track progress at the Next.js Changelog or GitHub Releases.
Next.js 16.2目前处于Canary版(最新版本:16.2.0-canary.84,2026年3月)。开发中的关键领域:
- 的Turbopack文件系统缓存 —— 将稳定版
next build的FS缓存扩展到生产构建,加快CI速度。next dev - Proxy优化 —— 对16.0版本中引入的(Node.js运行时替代
proxy.ts)进行持续迭代。Proxy API正在稳定,改进了请求上下文和流式支持。middleware.ts - React Compiler优化 —— 在16.0版本的稳定版React Compiler基础上,进一步改进自动记忆化。
注意:Canary版不推荐用于生产环境。可在Next.js Changelog或GitHub Releases跟踪进度。
DevTools MCP
DevTools MCP
Next.js 16 includes Next.js DevTools MCP, a Model Context Protocol integration for AI-assisted debugging. It enables AI agents to diagnose issues, explain behavior, and suggest fixes within your development workflow. If your AI coding assistant supports MCP, DevTools MCP can also automate upgrade and migration tasks.
Next.js 16包含Next.js DevTools MCP,这是一个用于AI辅助调试的Model Context Protocol集成。它使AI代理能够在您的开发工作流中诊断问题、解释行为并建议修复方案。如果您的AI编码助手支持MCP,DevTools MCP还可自动完成升级和迁移任务。
Breaking Changes in Next.js 16
Next.js 16中的破坏性变更
- Async Request APIs: ,
cookies(),headers(),paramsare all async — mustsearchParamsthemawait - Proxy replaces Middleware: Rename →
middleware.ts, runs on Node.js only (Edge not supported)proxy.ts - Turbopack is top-level config: Move from to
experimental.turbopackinturbopacknext.config - View Transitions: Built-in support for animating elements across navigations
- Node.js 20.9+ required: Dropped support for Node 18
- TypeScript 5.1+ required
- 异步请求API:、
cookies()、headers()、params均为异步——必须searchParams它们await - Proxy替代Middleware:将重命名为
middleware.ts,仅在Node.js运行时运行(不支持Edge)proxy.ts - Turbopack为顶层配置:从移至
experimental.turbopack中的next.configturbopack - 视图过渡:内置支持在导航间为元素添加动画
- 要求Node.js 20.9+:不再支持Node 18
- 要求TypeScript 5.1+
React 19 Gotchas
React 19的注意事项
useRef()
Requires an Initial Value
useRef()useRef()
需要初始值
useRef()React 19 strict mode enforces that must be called with an explicit initial value. Omitting it causes a type error or runtime warning:
useRef()tsx
// WRONG — React 19 strict mode complains
const ref = useRef() // ❌ missing initial value
const ref = useRef<HTMLDivElement>() // ❌ still missing
// CORRECT
const ref = useRef<HTMLDivElement>(null) // ✅
const ref = useRef(0) // ✅This affects all calls in client components. The fix is always to pass an explicit initial value (usually for DOM refs).
useRefnullReact 19严格模式强制要求必须传入显式初始值。省略初始值会导致类型错误或运行时警告:
useRef()tsx
// 错误写法 —— React 19严格模式会报错
const ref = useRef() // ❌ 缺少初始值
const ref = useRef<HTMLDivElement>() // ❌ 仍然缺少
// 正确写法
const ref = useRef<HTMLDivElement>(null) // ✅
const ref = useRef(0) // ✅这会影响客户端组件中的所有调用。修复方法始终是传入显式初始值(DOM引用通常为)。
useRefnullSecurity: Critical CVEs
安全:严重CVE漏洞
Multiple vulnerabilities affect all Next.js App Router applications (13.4+, 14.x, 15.x, 16.x). Upgrade immediately.
多个漏洞影响所有Next.js App Router应用(13.4+、14.x、15.x、16.x)。请立即升级。
CVE-2025-66478 / CVE-2025-55182 — Remote Code Execution (CVSS 10.0, Critical)
CVE-2025-66478 / CVE-2025-55182 —— 远程代码执行(CVSS 10.0,严重)
A deserialization vulnerability in the React Server Components (RSC) "Flight" protocol allows unauthenticated remote code execution via crafted HTTP requests. Near-100% exploit reliability against default configurations. Actively exploited in the wild. No workaround — upgrade is required. Rotate all application secrets after patching.
- Affects: Next.js App Router applications (15.x, 16.x, 14.3.0-canary.77+)
- Does NOT affect: Pages Router apps, Edge Runtime, Next.js 13.x, stable Next.js 14.x
React Server Components(RSC)“Flight”协议中的反序列化漏洞允许未经身份验证的攻击者通过精心构造的HTTP请求执行远程代码。默认配置下的利用成功率接近100%。已在野外被主动利用。 无临时解决方法——必须升级。补丁后请轮换所有应用密钥。
- 影响范围:Next.js App Router应用(15.x、16.x、14.3.0-canary.77+)
- 不影响:Pages Router应用、Edge Runtime、Next.js 13.x、稳定版Next.js 14.x
CVE-2025-55184 — Denial of Service (CVSS 7.5, High)
CVE-2025-55184 —— 拒绝服务(CVSS 7.5,高危)
Specially crafted HTTP requests cause the server process to hang indefinitely, consuming CPU and blocking legitimate users. No authentication required, low attack complexity.
精心构造的HTTP请求会导致服务器进程无限期挂起,消耗CPU并阻止合法用户访问。无需身份验证,攻击复杂度低。
CVE-2025-55183 — Source Code Exposure (CVSS 5.3, Medium)
CVE-2025-55183 —— 源代码泄露(CVSS 5.3,中危)
Malformed HTTP requests can trick Server Actions into returning their compiled source code instead of the expected response. Environment variables are not exposed, but any hardcoded secrets in Server Action code can leak.
格式错误的HTTP请求可诱使Server Actions返回编译后的源代码而非预期响应。环境变量不会泄露,但Server Action代码中硬编码的任何密钥可能会泄露。
CVE-2026-23864 — Denial of Service via Memory Exhaustion (CVSS 7.5, High)
CVE-2026-23864 —— 内存耗尽导致拒绝服务(CVSS 7.5,高危)
Disclosed January 26, 2026. The original DoS fix for CVE-2025-55184 was incomplete — additional vectors allow specially crafted HTTP requests to Server Function endpoints to crash the server via out-of-memory exceptions or excessive CPU usage. No authentication required.
2026年1月26日披露。针对CVE-2025-55184的原始DoS修复不完整——额外的攻击向量允许精心构造的HTTP请求发送到Server Function端点,通过内存不足异常或过高CPU使用率导致服务器崩溃。无需身份验证。
CVE-2025-29927 — Middleware Authorization Bypass (CVSS 9.1, Critical)
CVE-2025-29927 —— Middleware授权绕过(CVSS 9.1,严重)
An attacker can bypass middleware-based authorization by sending a crafted header, skipping all middleware logic. This affects any Next.js application that relies on (or in 16+) as the sole authorization gate. Patched in Next.js 14.2.25, 15.2.3, and all 16.x releases.
x-middleware-subrequestmiddleware.tsproxy.tsMitigation: Never rely on middleware/proxy as the only auth layer. Always re-validate authorization in Server Components, Server Actions, or Route Handlers. If you cannot patch immediately, block at your reverse proxy or WAF.
x-middleware-subrequest攻击者可通过发送精心构造的头绕过基于middleware的授权,跳过所有middleware逻辑。这会影响任何依赖(或16+版本中的)作为唯一授权网关的Next.js应用。已在Next.js 14.2.25、15.2.3和所有16.x版本中修复。
x-middleware-subrequestmiddleware.tsproxy.ts缓解措施:切勿依赖middleware/proxy作为唯一的认证层。始终在Server Components、Server Actions或Route Handlers中重新验证授权。如果无法立即补丁,请在反向代理或WAF中阻止头。
x-middleware-subrequestPatched Versions
已修复版本
| Release Line | Minimum Safe Version (all CVEs) |
|---|---|
| 14.x | |
| 15.0.x | |
| 15.1.x | |
| 15.2.x | |
| 15.3.x | |
| 15.4.x | |
| 15.5.x | |
| 16.0.x | |
| 16.1.x | |
Upgrade React to at least 19.0.1 / 19.1.2 / 19.2.1 for the RCE fix (CVE-2025-55182), and 19.2.4+ to fully address all DoS vectors (CVE-2025-55184, CVE-2025-67779, CVE-2026-23864).
bash
undefined| 发布分支 | 修复所有CVE的最低安全版本 |
|---|---|
| 14.x | |
| 15.0.x | |
| 15.1.x | |
| 15.2.x | |
| 15.3.x | |
| 15.4.x | |
| 15.5.x | |
| 16.0.x | |
| 16.1.x | |
将React升级到至少19.0.1 / 19.1.2 / 19.2.1以修复RCE漏洞(CVE-2025-55182),升级到**19.2.4+**以完全解决所有DoS向量(CVE-2025-55184、CVE-2025-67779、CVE-2026-23864)。
bash
undefinedUpgrade to latest patched versions
升级到最新的已修复版本
npm install next@latest react@latest react-dom@latest
Vercel deployed WAF rules automatically for hosted projects, but **WAF is defense-in-depth, not a substitute for patching**.npm install next@latest react@latest react-dom@latest
Vercel为托管项目自动部署了WAF规则,但**WAF是深度防御措施,不能替代补丁**。Rendering Strategy Decision
渲染策略决策
| Strategy | When to Use |
|---|---|
SSG ( | Content rarely changes, maximum performance |
ISR ( | Content changes periodically, acceptable staleness |
| SSR (Server Components) | Per-request fresh data, personalized content |
Cache Components ( | Mix static shell with dynamic parts |
| Client Components | Interactive UI, browser APIs needed |
| Streaming (Suspense) | Show content progressively as data loads |
| 策略 | 使用场景 |
|---|---|
SSG( | 内容很少变化,追求最高性能 |
ISR( | 内容定期变化,可接受一定的陈旧性 |
| SSR(Server Components) | 每个请求需要新鲜数据,个性化内容 |
Cache Components( | 静态外壳与动态部分混合 |
| Client Components | 交互式UI,需要浏览器API |
| 流式渲染(Suspense) | 数据加载时逐步显示内容 |
Rendering Strategy Guidance
渲染策略指南
Choosing a rendering strategy?
├─ Content changes less than once per day?
│ ├─ Same for all users? → SSG (`generateStaticParams`)
│ └─ Personalized? → SSG shell + client fetch for personalized parts
│
├─ Content changes frequently but can be slightly stale?
│ ├─ Revalidate on schedule? → ISR with `revalidate: N` seconds
│ └─ Revalidate on demand? → `revalidateTag()` or `revalidatePath()`
│
├─ Content must be fresh on every request?
│ ├─ Cacheable per-request? → Cache Components (`'use cache'` + `cacheLife`)
│ ├─ Personalized per-user? → SSR with Streaming (Suspense boundaries)
│ └─ Real-time? → Client-side with SWR/React Query + SSR for initial load
│
└─ Mostly static with one dynamic section?
└─ Partial Prerendering: static shell + Suspense for dynamic island如何选择渲染策略?
├─ 内容变化频率低于每天一次?
│ ├─ 所有用户看到的内容相同? → SSG(`generateStaticParams`)
│ └─ 内容个性化? → SSG外壳 + 客户端获取个性化部分
│
├─ 内容变化频繁但可接受轻微陈旧?
│ ├─ 按计划重新验证? → ISR + `revalidate: N`秒
│ └─ 按需重新验证? → `revalidateTag()`或`revalidatePath()`
│
├─ 每个请求都需要新鲜内容?
│ ├─ 每个请求可缓存? → Cache Components(`'use cache'` + `cacheLife`)
│ ├─ 每个用户个性化? → SSR + 流式渲染(Suspense边界)
│ └─ 实时内容? → 客户端使用SWR/React Query + SSR初始加载
│
└─ 大部分静态内容,仅一个动态部分?
└─ 部分预渲染:静态外壳 + Suspense动态区块Caching Strategy Matrix
缓存策略矩阵
| Data Type | Strategy | Implementation |
|---|---|---|
| Static assets (JS, CSS, images) | Immutable cache | Automatic with Vercel (hashed filenames) |
| API responses (shared) | Cache Components | |
| API responses (per-user) | No cache or short TTL | |
| Configuration data | Edge Config | |
| Database queries | ISR + on-demand | |
| Full pages | SSG / ISR | |
| Search results | Client-side + SWR | |
| 数据类型 | 策略 | 实现方式 |
|---|---|---|
| 静态资源(JS、CSS、图片) | 不可变缓存 | Vercel自动处理(带哈希的文件名) |
| API响应(共享) | Cache Components | |
| API响应(每个用户) | 无缓存或短TTL | |
| 配置数据 | Edge Config | |
| 数据库查询 | ISR + 按需重新验证 | 写入时调用 |
| 完整页面 | SSG / ISR | |
| 搜索结果 | 客户端 + SWR | |
Image Optimization Pattern
图片优化模式
tsx
// BEFORE: Unoptimized, causes LCP & CLS issues
<img src="/hero.jpg" />
// AFTER: Optimized with next/image
import Image from 'next/image';
<Image src="/hero.jpg" width={1200} height={600} priority alt="Hero" />tsx
// 优化前:未优化,导致LCP和CLS问题
<img src="/hero.jpg" />
// 优化后:使用next/image优化
import Image from 'next/image';
<Image src="/hero.jpg" width={1200} height={600} priority alt="Hero" />Font Loading Pattern
字体加载模式
tsx
// BEFORE: External font causes CLS
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet" />
// AFTER: Zero-CLS with next/font
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });tsx
// 优化前:外部字体导致CLS
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet" />
// 优化后:使用next/font实现零CLS
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });Cache Components Pattern
Cache Components模式
tsx
// BEFORE: Re-fetches on every request
async function ProductList() {
const products = await db.query('SELECT * FROM products');
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
// AFTER: Cached with automatic revalidation
'use cache';
import { cacheLife } from 'next/cache';
async function ProductList() {
cacheLife('hours');
const products = await db.query('SELECT * FROM products');
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}tsx
// 优化前:每个请求都重新获取
async function ProductList() {
const products = await db.query('SELECT * FROM products');
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
// 优化后:缓存并自动重新验证
'use cache';
import { cacheLife } from 'next/cache';
async function ProductList() {
cacheLife('hours');
const products = await db.query('SELECT * FROM products');
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}Optimistic UI Pattern
乐观UI模式
tsx
// Instant feedback while Server Action processes
'use client';
import { useOptimistic } from 'react';
function LikeButton({ count, onLike }) {
const [optimisticCount, addOptimistic] = useOptimistic(count);
return (
<button onClick={() => { addOptimistic(count + 1); onLike(); }}>
{optimisticCount} likes
</button>
);
}tsx
// Server Action处理时提供即时反馈
'use client';
import { useOptimistic } from 'react';
function LikeButton({ count, onLike }) {
const [optimisticCount, addOptimistic] = useOptimistic(count);
return (
<button onClick={() => { addOptimistic(count + 1); onLike(); }}>
{optimisticCount} 个赞
</button>
);
}OG Image Generation
OG图片生成
Next.js supports file-based OG image generation via and special files. These use (built on Satori) to render JSX to images at the Edge runtime.
opengraph-image.tsxtwitter-image.tsx@vercel/ogNext.js支持通过和特殊文件生成基于文件的OG图片。这些文件使用(基于Satori)在Edge运行时将JSX渲染为图片。
opengraph-image.tsxtwitter-image.tsx@vercel/ogFile Convention
文件约定
Place an (or ) in any route segment to auto-generate social images for that route:
opengraph-image.tsxtwitter-image.tsxtsx
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export const alt = 'Blog post'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export default async function Image({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json())
return new ImageResponse(
(
<div
style={{
fontSize: 48,
background: 'linear-gradient(to bottom, #000, #111)',
color: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 48,
}}
>
{post.title}
</div>
),
{ ...size }
)
}在任何路由片段中放置(或),即可自动为该路由生成社交图片:
opengraph-image.tsxtwitter-image.tsxtsx
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export const alt = '博客文章'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export default async function Image({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json())
return new ImageResponse(
(
<div
style={{
fontSize: 48,
background: 'linear-gradient(to bottom, #000, #111)',
color: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 48,
}}
>
{post.title}
</div>
),
{ ...size }
)
}Key Points
关键点
- — Import from
ImageResponse(re-exportsnext/og). Renders JSX to PNG/SVG images.@vercel/og - Edge runtime — OG image routes run on the Edge runtime by default. Export explicitly for clarity.
runtime = 'edge' - Exports — ,
alt, andsizeconfigure the generatedcontentTypetags automatically.<meta> - Static or dynamic — Without params, the image is generated at build time. With dynamic segments, it generates per-request.
- Supported CSS — Satori supports a Flexbox subset. Use inline objects (no Tailwind).
styleis required on containers.display: 'flex' - Fonts — Load custom fonts via and pass to
fetchoptions:ImageResponse.{ fonts: [{ name, data, style, weight }] } - Twitter fallback — If no exists,
twitter-image.tsxis used for Twitter cards too.opengraph-image.tsx
- —— 从
ImageResponse导入(重导出next/og)。将JSX渲染为PNG/SVG图片。@vercel/og - Edge运行时 —— OG图片路由默认在Edge运行时运行。显式导出以明确说明。
runtime = 'edge' - 导出内容 —— 、
alt和size会自动配置生成的contentType标签。<meta> - 静态或动态 —— 无参数时,图片在构建时生成。有动态片段时,每个请求生成一次。
- 支持的CSS —— Satori支持Flexbox子集。使用内联对象(不支持Tailwind)。容器必须使用
style。display: 'flex' - 字体 —— 通过加载自定义字体,并传递给
fetch选项:ImageResponse。{ fonts: [{ name, data, style, weight }] } - Twitter回退 —— 如果没有,
twitter-image.tsx会被用作Twitter卡片的图片。opengraph-image.tsx
When to Use
使用场景
| Approach | When |
|---|---|
| Dynamic per-route OG images with data fetching |
Static | Same image for every page in a segment |
| Point to an external image URL |
| 方式 | 场景 |
|---|---|
| 带数据获取的动态路由OG图片 |
静态 | 片段中所有页面使用相同图片 |
| 指向外部图片URL |
Deployment on Vercel
在Vercel上部署
- Zero-config: Vercel auto-detects Next.js and optimizes
- for local development with Vercel features
vercel dev - Server Components → Serverless/Edge Functions automatically
- Image optimization via (automatic on Vercel)
next/image - Font optimization via (automatic on Vercel)
next/font
- 零配置:Vercel自动检测Next.js并进行优化
- 用于本地开发,支持Vercel特性
vercel dev - Server Components自动转换为Serverless/Edge Functions
- 通过进行图片优化(Vercel上自动生效)
next/image - 通过进行字体优化(Vercel上自动生效)
next/font
Common Patterns
常见模式
Data Fetching in Server Components
Server Components中的数据获取
tsx
// Parallel data fetching
const [users, posts] = await Promise.all([
getUsers(),
getPosts(),
])tsx
// 并行数据获取
const [users, posts] = await Promise.all([
getUsers(),
getPosts(),
])Streaming with Suspense
使用Suspense进行流式渲染
tsx
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<Skeleton />}>
<SlowDataComponent />
</Suspense>
</div>
)
}tsx
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<h1>仪表板</h1>
<Suspense fallback={<Skeleton />}>
<SlowDataComponent />
</Suspense>
</div>
)
}Error Handling
错误处理
tsx
// app/dashboard/error.tsx
'use client'
export default function Error({ error, reset }: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>Something went wrong</h2>
<button onClick={() => reset()}>Try again</button>
</div>
)
}tsx
// app/dashboard/error.tsx
'use client'
export default function Error({ error, reset }: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>出现错误</h2>
<button onClick={() => reset()}>重试</button>
</div>
)
}