nextjs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Next.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
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.
切勿在模块作用域初始化数据库客户端(Neon、Drizzle)、Redis(Upstash)或服务SDK(Resend、Slack)。
在执行
next build
时,静态生成会在运行时环境变量加载前解析模块,这会导致启动崩溃。请始终在 getter 函数内部延迟初始化这些客户端。
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
}
对Redis和SDK客户端(
getRedis()
getResend()
getSlackClient()
)应用相同的延迟单例模式,而非在导入时创建它们。

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 (
.claude/
,
CLAUDE.md
,
.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
运行
create-next-app
时,务必传入
--yes
以跳过交互式提示(React Compiler、导入别名),这些提示在非交互式shell中会导致进程挂起:
bash
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/
、配置文件等),您必须添加
--force
。如果不添加,
create-next-app
会因“目录中存在可能冲突的文件”而失败并阻止脚手架创建。请先检查目录——如果目录中有任何内容,请使用
--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)

Geist字体修复(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;
}
  1. 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.
shadcn init
会重写
globals.css
,在
@theme inline
中添加
--font-sans: var(--font-sans)
——这是一个循环引用,会回退到Times/衬线字体。即使使用
var(--font-geist-sans)
也无法生效,因为Tailwind v4的
@theme inline
CSS解析阶段而非运行时解析。
create-next-app
+
shadcn init
后需要两个修复:
  1. globals.css
    中使用字面字体名称
    (而非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;
}
  1. 将字体变量类名从
    <body>
    移至
    <html>
    (在layout.tsx中):
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-app
+
shadcn init
后,请务必同时应用这两个修复。

UI 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:
    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).
在此技术栈中构建页面、布局和路由级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
app/
). Ensure React 19.2.4+ for security patches (see CVE section below).
Next.js 16使用React 19.2特性和App Router(
app/
目录下的文件系统路由)。请确保使用React **19.2.4+**以获取安全补丁(参见下方CVE部分)。

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
  • layout.tsx
    —— 持久化包装器,在导航间保留状态
  • page.tsx
    —— 路由的唯一UI,使路由可公开访问
  • loading.tsx
    —— 片段加载时显示的Suspense回退UI
  • error.tsx
    —— 片段的错误边界
  • not-found.tsx
    —— 片段的404 UI
  • route.ts
    —— API端点(Route Handler)
  • template.tsx
    —— 类似布局,但在导航时重新挂载
  • default.tsx
    —— 并行路由的回退UI

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
  • 动态片段:
    [id]
    ,捕获所有片段:
    [...slug]
    ,可选捕获所有片段:
    [[...slug]]
  • 路由组:
    (group)
    —— 组织路由但不影响URL
  • 并行路由:
    @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
    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} />
}
App Router中的所有组件默认都是Server Components。它们:
  • 仅在服务器端运行,不会向客户端发送任何JavaScript
  • 可直接
    await
    数据(fetch、数据库查询、文件系统)
  • 无法使用
    useState
    useEffect
    或浏览器API
  • 无法使用事件处理程序(
    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
'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.
当需要交互性或浏览器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>
}
规则:将
'use client'
尽可能向下推到组件树中。将数据获取保留在Server Components中,并通过props传递数据。

Server Actions / Server Functions

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
标记有
'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
'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} />
}
'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'
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'
VariantShared across deployments?Shared across users?Use case
'use cache'
No (per-deployment)YesDefault, most use cases
'use cache: remote'
YesYesExpensive computations shared globally
'use cache: private'
NoNoUser-specific cached data (e.g., profile)
'use cache'
支持范围修饰符,用于控制缓存数据的存储位置:
tsx
// 默认 —— 缓存在部署的本地数据缓存中
'use cache'

// 远程缓存 —— 在所有部署和区域间共享(仅Vercel支持)
'use cache: remote'

// 私有缓存 —— 每个请求独立缓存,不会在用户间共享
'use cache: private'
变体是否在部署间共享是否在用户间共享使用场景
'use cache'
否(每个部署独立)默认值,大多数使用场景
'use cache: remote'
需全局共享的昂贵计算
'use cache: private'
用户特定的缓存数据(如个人资料)

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.
Next.js 16使用
cacheHandlers
(复数形式)为不同缓存类型配置独立的处理器:
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'),
  },
}
重要提示
cacheHandlers
(复数形式,Next.js 16+)替代了
cacheHandler
(单数形式,Next.js 15)。单数形式为所有缓存类型配置一个处理器,复数形式允许为不同类型配置独立处理器(
default
remote
fetch
)。在Next.js 16中使用旧的单数
cacheHandler
会触发弃用警告。

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'
).
FunctionContextBehavior
updateTag(tag)
Server Actions onlyImmediate expiration, read-your-own-writes
revalidateTag(tag, 'max')
Server Actions + Route HandlersStale-while-revalidate (recommended)
revalidateTag(tag, { expire: 0 })
Route Handlers (webhooks)Immediate expiration from external triggers
在Server Action中使用
updateTag('users')
(立即过期,仅Server Actions支持),或在Server Actions或Route Handlers中使用
revalidateTag('users', 'max')
实现 stale-while-revalidate 策略。
重要提示:单参数
revalidateTag(tag)
在Next.js 16中已弃用。请始终传递
cacheLife
配置文件作为第二个参数(例如
'max'
'hours'
'days'
)。
函数上下文行为
updateTag(tag)
仅Server Actions立即过期,读己写(read-your-own-writes)
revalidateTag(tag, 'max')
Server Actions + Route HandlersStale-while-revalidate(推荐)
revalidateTag(tag, { expire: 0 })
Route Handlers(webhook)从外部触发立即过期

Proxy (formerly Middleware)

Proxy(原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*'] }
在Next.js 16中,
middleware.ts
更名为
proxy.ts
。它仅在Node.js运行时运行——Edge运行时不支持proxy,无法配置。
文件位置:将
proxy.ts
放在与
app/
目录同级的位置:
  • 标准项目:
    proxy.ts
    在项目根目录
  • 使用
    --src-dir
    (或
    srcDir: true
    ):
    src/proxy.ts
    (在
    src/
    目录内,与
    app/
    同级)
如果将
proxy.ts
放在错误位置,Next.js会静默忽略它,不会进行请求拦截。
限制
  • Proxy仅能重写重定向修改请求头——无法返回完整响应体。此类场景请使用Route Handlers。
  • 配置标志已重命名:
    skipMiddlewareUrlNormalize
    skipProxyUrlNormalize
  • 保持轻量化:用于高级流量控制(例如重定向无会话Cookie的用户)。详细的认证逻辑应放在Server Components或Server Actions中。
  • OpenNext注意事项:OpenNext目前不支持
    proxy.ts
    ——如果使用OpenNext自托管,请继续使用
    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 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.
使用内置的升级命令(从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:
  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.
Next.js 16.1(2025年12月,最新稳定补丁:16.1.6)基于16.0版本,提升了开发者体验:
  1. Turbopack文件系统缓存(稳定版) —— 编译器产物现在会在
    next dev
    重启之间缓存到磁盘,大型项目的启动速度最高可提升14倍。默认启用,无需配置。
    next build
    的文件系统缓存计划后续推出。
  2. Bundle Analyzer(实验性) —— 新的内置Bundle Analyzer与Turbopack兼容。提供路由特定过滤、导入追踪和RSC边界分析,可识别服务器和客户端包中的臃肿依赖。在
    next.config
    中启用
    experimental.bundleAnalyzer: true
    即可使用。
  3. next dev --inspect
    —— 无需全局设置
    NODE_OPTIONS=--inspect
    即可调试开发服务器。仅对相关进程应用调试器。
  4. next upgrade
    命令
    —— 新的CLI命令简化版本升级:
    npx @next/codemod@canary upgrade latest
  5. 传递外部依赖 —— Turbopack可正确解析并外部化
    serverExternalPackages
    中的传递依赖,无需额外配置。
  6. 安装包体积减小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

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.
创建一个稳定的函数,始终访问最新的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>
组件

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>
        <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
mode="hidden"
, effects are suspended (not running in the background).
在隐藏和显示UI时保留组件状态,无需卸载。解决了经典的两难问题:卸载会丢失状态,而CSS
display:none
会让effect继续运行:
tsx
'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>
  )
}
适用于:标签页界面、模态框、侧边栏、后台任务——任何需要保持组件状态而无需始终渲染的场景。当
mode="hidden"
时,effect会被暂停(不会在后台运行)。

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:
useId
now generates IDs with
_r_
prefix (instead of
:r:
) to be valid for
view-transition-name
and XML 1.0 names.
React 19.2支持浏览器的View Transitions API,用于在导航间为元素添加动画。Next.js 16内置支持——元素可在路由变化时添加动画,无需手动编写过渡逻辑。
关键变化:
useId
现在生成带有
_r_
前缀的ID(而非
:r:
),以符合
view-transition-name
和XML 1.0名称的要求。

Layout Deduplication During Prefetching

预取期间的布局去重

Next.js 16 deduplicates shared layouts during prefetching. When multiple
<Link>
components point to routes with a shared layout, the layout is downloaded once instead of separately for each 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
)

Bundle Analyzer(
next experimental-analyze

Built-in bundle analyzer that works with Turbopack (available since Next.js 16.1):
bash
undefined
与Turbopack兼容的内置Bundle Analyzer(从Next.js 16.1开始可用):
bash
undefined

Analyze 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:
  1. Turbopack File System Caching for
    next build
    — Extending the stable
    next dev
    FS cache to production builds for faster CI.
  2. Proxy refinements — Continued iteration on
    proxy.ts
    (the Node.js-runtime replacement for
    middleware.ts
    introduced in 16.0). The proxy API is stabilizing with improved request context and streaming support.
  3. 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月)。开发中的关键领域:
  1. next build
    的Turbopack文件系统缓存
    —— 将稳定版
    next dev
    的FS缓存扩展到生产构建,加快CI速度。
  2. Proxy优化 —— 对16.0版本中引入的
    proxy.ts
    (Node.js运行时替代
    middleware.ts
    )进行持续迭代。Proxy API正在稳定,改进了请求上下文和流式支持。
  3. React Compiler优化 —— 在16.0版本的稳定版React Compiler基础上,进一步改进自动记忆化。
注意:Canary版不推荐用于生产环境。可在Next.js ChangelogGitHub 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中的破坏性变更

  1. Async Request APIs:
    cookies()
    ,
    headers()
    ,
    params
    ,
    searchParams
    are all async — must
    await
    them
  2. Proxy replaces Middleware: Rename
    middleware.ts
    proxy.ts
    , runs on Node.js only (Edge not supported)
  3. Turbopack is top-level config: Move from
    experimental.turbopack
    to
    turbopack
    in
    next.config
  4. View Transitions: Built-in support for animating elements across navigations
  5. Node.js 20.9+ required: Dropped support for Node 18
  6. TypeScript 5.1+ required
  1. 异步请求API
    cookies()
    headers()
    params
    searchParams
    均为异步——必须
    await
    它们
  2. Proxy替代Middleware:将
    middleware.ts
    重命名为
    proxy.ts
    ,仅在Node.js运行时运行(不支持Edge)
  3. Turbopack为顶层配置:从
    experimental.turbopack
    移至
    next.config
    中的
    turbopack
  4. 视图过渡:内置支持在导航间为元素添加动画
  5. 要求Node.js 20.9+:不再支持Node 18
  6. 要求TypeScript 5.1+

React 19 Gotchas

React 19的注意事项

useRef()
Requires an Initial Value

useRef()
需要初始值

React 19 strict mode enforces that
useRef()
must be called with an explicit initial value. Omitting it causes a type error or runtime warning:
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
useRef
calls in client components. The fix is always to pass an explicit initial value (usually
null
for DOM refs).
React 19严格模式强制要求
useRef()
必须传入显式初始值。省略初始值会导致类型错误或运行时警告:
tsx
// 错误写法 —— React 19严格模式会报错
const ref = useRef()       // ❌ 缺少初始值
const ref = useRef<HTMLDivElement>()  // ❌ 仍然缺少

// 正确写法
const ref = useRef<HTMLDivElement>(null)  // ✅
const ref = useRef(0)                     // ✅
这会影响客户端组件中的所有
useRef
调用。修复方法始终是传入显式初始值(DOM引用通常为
null
)。

Security: 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
x-middleware-subrequest
header, skipping all middleware logic. This affects any Next.js application that relies on
middleware.ts
(or
proxy.ts
in 16+) as the sole authorization gate. Patched in Next.js 14.2.25, 15.2.3, and all 16.x releases.
Mitigation: 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
x-middleware-subrequest
at your reverse proxy or WAF.
攻击者可通过发送精心构造的
x-middleware-subrequest
头绕过基于middleware的授权,跳过所有middleware逻辑。这会影响任何依赖
middleware.ts
(或16+版本中的
proxy.ts
)作为唯一授权网关的Next.js应用。已在Next.js 14.2.25、15.2.3和所有16.x版本中修复。
缓解措施:切勿依赖middleware/proxy作为唯一的认证层。始终在Server Components、Server Actions或Route Handlers中重新验证授权。如果无法立即补丁,请在反向代理或WAF中阻止
x-middleware-subrequest
头。

Patched Versions

已修复版本

Release LineMinimum Safe Version (all CVEs)
14.x
next@14.2.35
15.0.x
next@15.0.5
15.1.x
next@15.1.9
15.2.x
next@15.2.6
15.3.x
next@15.3.6
15.4.x
next@15.4.8
15.5.x
next@15.5.7
16.0.x
next@16.0.11
16.1.x
next@16.1.5
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
next@14.2.35
15.0.x
next@15.0.5
15.1.x
next@15.1.9
15.2.x
next@15.2.6
15.3.x
next@15.3.6
15.4.x
next@15.4.8
15.5.x
next@15.5.7
16.0.x
next@16.0.11
16.1.x
next@16.1.5
将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
undefined

Upgrade 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

渲染策略决策

StrategyWhen to Use
SSG (
generateStaticParams
)
Content rarely changes, maximum performance
ISR (
revalidate: N
)
Content changes periodically, acceptable staleness
SSR (Server Components)Per-request fresh data, personalized content
Cache Components (
'use cache'
)
Mix static shell with dynamic parts
Client ComponentsInteractive UI, browser APIs needed
Streaming (Suspense)Show content progressively as data loads
策略使用场景
SSG(
generateStaticParams
内容很少变化,追求最高性能
ISR(
revalidate: N
内容定期变化,可接受一定的陈旧性
SSR(Server Components)每个请求需要新鲜数据,个性化内容
Cache Components(
'use cache'
静态外壳与动态部分混合
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 TypeStrategyImplementation
Static assets (JS, CSS, images)Immutable cacheAutomatic with Vercel (hashed filenames)
API responses (shared)Cache Components
'use cache'
+
cacheLife('hours')
API responses (per-user)No cache or short TTL
cacheLife({ revalidate: 60 })
with user-scoped key
Configuration dataEdge Config
@vercel/edge-config
(< 5ms reads)
Database queriesISR + on-demand
revalidateTag('products')
on write
Full pagesSSG / ISR
generateStaticParams
+
revalidate
Search resultsClient-side + SWR
useSWR
with stale-while-revalidate
数据类型策略实现方式
静态资源(JS、CSS、图片)不可变缓存Vercel自动处理(带哈希的文件名)
API响应(共享)Cache Components
'use cache'
+
cacheLife('hours')
API响应(每个用户)无缓存或短TTL
cacheLife({ revalidate: 60 })
+ 用户范围的键
配置数据Edge Config
@vercel/edge-config
(读取延迟<5ms)
数据库查询ISR + 按需重新验证写入时调用
revalidateTag('products')
完整页面SSG / ISR
generateStaticParams
+
revalidate
搜索结果客户端 + SWR
useSWR
+ stale-while-revalidate

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
opengraph-image.tsx
and
twitter-image.tsx
special files. These use
@vercel/og
(built on Satori) to render JSX to images at the Edge runtime.
Next.js支持通过
opengraph-image.tsx
twitter-image.tsx
特殊文件生成基于文件的OG图片。这些文件使用
@vercel/og
(基于Satori)在Edge运行时将JSX渲染为图片。

File Convention

文件约定

Place an
opengraph-image.tsx
(or
twitter-image.tsx
) in any route segment to auto-generate social images for that route:
tsx
// 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.tsx
(或
twitter-image.tsx
),即可自动为该路由生成社交图片:
tsx
// 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

关键点

  • ImageResponse
    — Import from
    next/og
    (re-exports
    @vercel/og
    ). Renders JSX to PNG/SVG images.
  • Edge runtime — OG image routes run on the Edge runtime by default. Export
    runtime = 'edge'
    explicitly for clarity.
  • Exports
    alt
    ,
    size
    , and
    contentType
    configure the generated
    <meta>
    tags automatically.
  • 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
    style
    objects (no Tailwind).
    display: 'flex'
    is required on containers.
  • Fonts — Load custom fonts via
    fetch
    and pass to
    ImageResponse
    options:
    { fonts: [{ name, data, style, weight }] }
    .
  • Twitter fallback — If no
    twitter-image.tsx
    exists,
    opengraph-image.tsx
    is used for Twitter cards too.
  • ImageResponse
    —— 从
    next/og
    导入(重导出
    @vercel/og
    )。将JSX渲染为PNG/SVG图片。
  • Edge运行时 —— OG图片路由默认在Edge运行时运行。显式导出
    runtime = 'edge'
    以明确说明。
  • 导出内容 ——
    alt
    size
    contentType
    会自动配置生成的
    <meta>
    标签。
  • 静态或动态 —— 无参数时,图片在构建时生成。有动态片段时,每个请求生成一次。
  • 支持的CSS —— Satori支持Flexbox子集。使用内联
    style
    对象(不支持Tailwind)。容器必须使用
    display: 'flex'
  • 字体 —— 通过
    fetch
    加载自定义字体,并传递给
    ImageResponse
    选项:
    { fonts: [{ name, data, style, weight }] }
  • Twitter回退 —— 如果没有
    twitter-image.tsx
    opengraph-image.tsx
    会被用作Twitter卡片的图片。

When to Use

使用场景

ApproachWhen
opengraph-image.tsx
file
Dynamic per-route OG images with data fetching
Static
opengraph-image.png
file
Same image for every page in a segment
generateMetadata
with
openGraph.images
Point to an external image URL
方式场景
opengraph-image.tsx
文件
带数据获取的动态路由OG图片
静态
opengraph-image.png
文件
片段中所有页面使用相同图片
generateMetadata
+
openGraph.images
指向外部图片URL

Deployment on Vercel

在Vercel上部署

  • Zero-config: Vercel auto-detects Next.js and optimizes
  • vercel dev
    for local development with Vercel features
  • Server Components → Serverless/Edge Functions automatically
  • Image optimization via
    next/image
    (automatic on Vercel)
  • Font optimization via
    next/font
    (automatic on Vercel)
  • 零配置:Vercel自动检测Next.js并进行优化
  • vercel dev
    用于本地开发,支持Vercel特性
  • Server Components自动转换为Serverless/Edge Functions
  • 通过
    next/image
    进行图片优化(Vercel上自动生效)
  • 通过
    next/font
    进行字体优化(Vercel上自动生效)

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>
  )
}

Official Documentation

官方文档