nextjs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Next.js App Router - Production Patterns

Next.js App Router - 生产环境实践模式

Version: Next.js 16.1.1 React Version: 19.2.3 Node.js: 20.9+ Last Verified: 2026-01-09

版本: Next.js 16.1.1 React版本: 19.2.3 Node.js: 20.9+ 最后验证时间: 2026-01-09

Table of Contents

目录

When to Use This Skill

何时使用本技能

Focus: Next.js 16 breaking changes and knowledge gaps (December 2024+).
Use this skill when you need:
  • Next.js 16 breaking changes (async params, proxy.ts, parallel routes default.js, removed features)
  • Cache Components with
    "use cache"
    directive (NEW in Next.js 16)
  • New caching APIs:
    revalidateTag()
    ,
    updateTag()
    ,
    refresh()
    (Updated in Next.js 16)
  • Migration from Next.js 15 to 16 (avoid breaking change errors)
  • Async route params (
    params
    ,
    searchParams
    ,
    cookies()
    ,
    headers()
    now async)
  • Parallel routes with default.js (REQUIRED in Next.js 16)
  • React 19.2 features (View Transitions,
    useEffectEvent()
    , React Compiler)
  • Turbopack (stable and default in Next.js 16)
  • Image defaults changed (TTL, sizes, qualities in Next.js 16)
  • Error prevention (25 documented Next.js 16 errors with solutions)

核心聚焦: Next.js 16的破坏性变更与知识盲区(2024年12月及以后)。
在以下场景中使用本技能:
  • Next.js 16 破坏性变更(异步参数、proxy.ts、并行路由default.js、已移除特性)
  • 使用
    "use cache"
    指令的缓存组件
    (Next.js 16 新增特性)
  • 新缓存API:
    revalidateTag()
    updateTag()
    refresh()
    (Next.js 16 中已更新)
  • 从Next.js 15迁移到16(避免破坏性变更导致的错误)
  • 异步路由参数
    params
    searchParams
    cookies()
    headers()
    现在为异步)
  • 包含default.js的并行路由(Next.js 16 中为必填项)
  • React 19.2 特性(View Transitions、
    useEffectEvent()
    、React Compiler)
  • Turbopack(Next.js 16 中已稳定并成为默认选项)
  • Image默认配置变更(Next.js 16 中的TTL、尺寸、质量设置)
  • 错误预防(25种已记录的Next.js 16错误及对应解决方案)

When NOT to Use This Skill

何时不使用本技能

Do NOT use this skill for:
  • Cloudflare Workers deployment → Use
    cloudflare-nextjs
    skill instead
  • Pages Router patterns → This skill covers App Router ONLY (Pages Router is legacy)
  • Authentication libraries → Use
    clerk-auth
    ,
    better-auth
    , or other auth-specific skills
  • Database integration → Use
    cloudflare-d1
    ,
    drizzle-orm-d1
    , or database-specific skills
  • UI component libraries → Use
    tailwind-v4-shadcn
    skill for Tailwind + shadcn/ui
  • State management → Use
    zustand-state-management
    ,
    tanstack-query
    skills
  • Form libraries → Use
    react-hook-form-zod
    skill
  • Vercel-specific features → Refer to Vercel platform documentation
  • Next.js Enterprise features (ISR, DPR) → Refer to Next.js Enterprise docs
  • Deployment configuration → Use platform-specific deployment skills
Relationship with Other Skills:
  • cloudflare-nextjs: For deploying Next.js to Cloudflare Workers (use BOTH skills together if deploying to Cloudflare)
  • tailwind-v4-shadcn: For Tailwind v4 + shadcn/ui setup (composable with this skill)
  • clerk-auth: For Clerk authentication in Next.js (composable with this skill)
  • better-auth: For Better Auth integration (composable with this skill)

请勿在以下场景使用本技能:
  • Cloudflare Workers 部署 → 请使用
    cloudflare-nextjs
    技能
  • Pages Router 实践 → 本技能仅覆盖App Router(Pages Router已为遗留方案)
  • 认证库 → 请使用
    clerk-auth
    better-auth
    或其他专注于认证的技能
  • 数据库集成 → 请使用
    cloudflare-d1
    drizzle-orm-d1
    或其他数据库专属技能
  • UI组件库 → 请使用
    tailwind-v4-shadcn
    技能处理Tailwind + shadcn/ui相关内容
  • 状态管理 → 请使用
    zustand-state-management
    tanstack-query
    技能
  • 表单库 → 请使用
    react-hook-form-zod
    技能
  • Vercel专属特性 → 请参考Vercel平台官方文档
  • Next.js企业级特性(ISR、DPR)→ 请参考Next.js企业级文档
  • 部署配置 → 请使用平台专属的部署技能
与其他技能的关联:
  • cloudflare-nextjs: 用于将Next.js部署到Cloudflare Workers(若部署到Cloudflare,可同时使用这两个技能)
  • tailwind-v4-shadcn: 用于Tailwind v4 + shadcn/ui的配置(可与本技能组合使用)
  • clerk-auth: 用于在Next.js中集成Clerk认证(可与本技能组合使用)
  • better-auth: 用于集成Better Auth(可与本技能组合使用)

Security Advisories (December 2025)

安全公告(2025年12月)

CRITICAL: Three security vulnerabilities were disclosed in December 2025 affecting Next.js with React Server Components:
CVESeverityAffectedDescription
CVE-2025-66478CRITICAL (10.0)15.x, 16.xServer Component arbitrary code execution
CVE-2025-55184HIGH13.x-16.xDenial of Service via malformed request
CVE-2025-55183MEDIUM13.x-16.xSource code exposure in error responses
Action Required: Upgrade to Next.js 16.1.1 or later immediately.
bash
npm update next
严重警告: 2025年12月披露了三个影响使用React Server Components的Next.js应用的安全漏洞:
CVE编号严重程度影响版本描述
CVE-2025-66478严重(10.0)15.x、16.xServer Component任意代码执行漏洞
CVE-2025-5518413.x-16.x畸形请求导致的拒绝服务漏洞
CVE-2025-5518313.x-16.x错误响应中暴露源代码漏洞
必须执行的操作: 立即升级到Next.js 16.1.1或更高版本。
bash
npm update next

Verify: npm list next should show 16.1.1+

验证:npm list next应显示16.1.1+


**References**:
- https://nextjs.org/security
- https://github.com/vercel/next.js/security/advisories

---

**参考链接**:
- https://nextjs.org/security
- https://github.com/vercel/next.js/security/advisories

---

Next.js 16.1 Updates (December 2025)

Next.js 16.1 更新内容(2025年12月)

New in 16.1:
  • Turbopack File System Caching (STABLE): Now enabled by default in development
  • Next.js Bundle Analyzer: New experimental feature for bundle analysis
  • Improved Debugging: Enhanced
    next dev --inspect
    support
  • Security Fixes: Addresses CVE-2025-66478, CVE-2025-55184, CVE-2025-55183

16.1版本新增特性:
  • Turbopack文件系统缓存(已稳定): 现在在开发环境中默认启用
  • Next.js Bundle Analyzer: 用于包分析的新实验性功能
  • 改进的调试支持: 增强了
    next dev --inspect
    的支持
  • 安全修复: 修复了CVE-2025-66478、CVE-2025-55184、CVE-2025-55183漏洞

Next.js 16 Breaking Changes

Next.js 16 破坏性变更

IMPORTANT: Next.js 16 introduces multiple breaking changes. Read this section carefully if migrating from Next.js 15 or earlier.
重要提示: Next.js 16引入了多项破坏性变更。如果从Next.js 15或更早版本迁移,请仔细阅读本节内容。

1. Async Route Parameters (BREAKING)

1. 异步路由参数(破坏性变更)

Breaking Change:
params
,
searchParams
,
cookies()
,
headers()
,
draftMode()
are now async and must be awaited.
Before (Next.js 15):
typescript
// ❌ This no longer works in Next.js 16
export default function Page({ params, searchParams }: {
  params: { slug: string }
  searchParams: { query: string }
}) {
  const slug = params.slug // ❌ Error: params is a Promise
  const query = searchParams.query // ❌ Error: searchParams is a Promise
  return <div>{slug}</div>
}
After (Next.js 16):
typescript
// ✅ Correct: await params and searchParams
export default async function Page({ params, searchParams }: {
  params: Promise<{ slug: string }>
  searchParams: Promise<{ query: string }>
}) {
  const { slug } = await params // ✅ Await the promise
  const { query } = await searchParams // ✅ Await the promise
  return <div>{slug}</div>
}
Applies to:
  • params
    in pages, layouts, route handlers
  • searchParams
    in pages
  • cookies()
    from
    next/headers
  • headers()
    from
    next/headers
  • draftMode()
    from
    next/headers
Migration:
typescript
// ❌ Before
import { cookies, headers } from 'next/headers'

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

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

export async function MyComponent() {
  const cookieStore = await cookies() // ✅ Async access
  const headersList = await headers() // ✅ Async access
}
Codemod: Run
npx @next/codemod@canary upgrade latest
to automatically migrate.
Codemod Limitations (Community-sourced): The official codemod handles ~80% of async API migrations but misses edge cases:
  • Async APIs accessed in custom hooks
  • Conditional logic accessing params
  • Components imported from external packages
  • Complex server actions with multiple async calls
After running the codemod, search for
@next-codemod-error
comments marking places it couldn't auto-fix.
Manual Migration for Client Components:
typescript
// For client components, use React.use() to unwrap promises
'use client';

import { use } from 'react';

export default function ClientComponent({
  params
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = use(params); // Unwrap Promise in client
  return <div>{id}</div>;
}
See Template:
templates/app-router-async-params.tsx

变更内容:
params
searchParams
cookies()
headers()
draftMode()
现在为异步,必须使用await等待。
Next.js 15中的写法:
typescript
// ❌ 在Next.js 16中不再生效
export default function Page({ params, searchParams }: {
  params: { slug: string }
  searchParams: { query: string }
}) {
  const slug = params.slug // ❌ 错误:params是一个Promise
  const query = searchParams.query // ❌ 错误:searchParams是一个Promise
  return <div>{slug}</div>
}
Next.js 16中的正确写法:
typescript
// ✅ 正确:await params和searchParams
export default async function Page({ params, searchParams }: {
  params: Promise<{ slug: string }>
  searchParams: Promise<{ query: string }>
}) {
  const { slug } = await params // ✅ 等待Promise完成
  const { query } = await searchParams // ✅ 等待Promise完成
  return <div>{slug}</div>
}
适用范围:
  • 页面、布局、路由处理器中的
    params
  • 页面中的
    searchParams
  • next/headers
    中的
    cookies()
  • next/headers
    中的
    headers()
  • next/headers
    中的
    draftMode()
迁移方法:
typescript
// ❌ 旧写法
import { cookies, headers } from 'next/headers'

export function MyComponent() {
  const cookieStore = cookies() // ❌ 同步访问
  const headersList = headers() // ❌ 同步访问
}

// ✅ 新写法
import { cookies, headers } from 'next/headers'

export async function MyComponent() {
  const cookieStore = await cookies() // ✅ 异步访问
  const headersList = await headers() // ✅ 异步访问
}
代码迁移工具: 运行
npx @next/codemod@canary upgrade latest
自动完成迁移。
代码迁移工具的局限性(社区收集): 官方代码迁移工具可处理约80%的异步API迁移,但会遗漏一些边缘情况:
  • 自定义钩子中访问的异步API
  • 条件逻辑中访问的参数
  • 从外部包导入的组件
  • 包含多个异步调用的复杂Server Actions
运行代码迁移工具后,搜索
@next-codemod-error
注释,这些标记的位置是工具无法自动修复的地方。
客户端组件的手动迁移方法:
typescript
// 客户端组件中,使用React.use()解包Promise
'use client';

import { use } from 'react';

export default function ClientComponent({
  params
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = use(params); // 在客户端解包Promise
  return <div>{id}</div>;
}
参考模板:
templates/app-router-async-params.tsx

2. Middleware → Proxy Migration (BREAKING)

2. Middleware → Proxy 迁移(破坏性变更)

Breaking Change:
middleware.ts
is deprecated in Next.js 16. Use
proxy.ts
instead.
Why the Change:
proxy.ts
makes the network boundary explicit by running on Node.js runtime (not Edge runtime). This provides better clarity between edge middleware and server-side proxies.
Migration Steps:
  1. Rename file:
    middleware.ts
    proxy.ts
  2. Rename function:
    middleware
    proxy
  3. Update config:
    matcher
    config.matcher
    (same syntax)
Before (Next.js 15):
typescript
// middleware.ts ❌ Deprecated in Next.js 16
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const response = NextResponse.next()
  response.headers.set('x-custom-header', 'value')
  return response
}

export const config = {
  matcher: '/api/:path*',
}
After (Next.js 16):
typescript
// proxy.ts ✅ New in Next.js 16
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function proxy(request: NextRequest) {
  const response = NextResponse.next()
  response.headers.set('x-custom-header', 'value')
  return response
}

export const config = {
  matcher: '/api/:path*',
}
Note:
middleware.ts
still works in Next.js 16 but is deprecated. Migrate to
proxy.ts
for future compatibility.
See Template:
templates/proxy-migration.ts
See Reference:
references/proxy-vs-middleware.md

变更内容:
middleware.ts
在Next.js 16中已被废弃,请使用
proxy.ts
替代。
变更原因:
proxy.ts
通过在Node.js运行时(而非Edge运行时)运行,明确了网络边界。这使得边缘中间件和服务器端代理的区分更加清晰。
迁移步骤:
  1. 重命名文件:
    middleware.ts
    proxy.ts
  2. 重命名函数:
    middleware
    proxy
  3. 更新配置:
    matcher
    config.matcher
    (语法保持不变)
Next.js 15中的写法:
typescript
// middleware.ts ❌ 在Next.js 16中已废弃
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const response = NextResponse.next()
  response.headers.set('x-custom-header', 'value')
  return response
}

export const config = {
  matcher: '/api/:path*',
}
Next.js 16中的正确写法:
typescript
// proxy.ts ✅ Next.js 16中的新写法
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function proxy(request: NextRequest) {
  const response = NextResponse.next()
  response.headers.set('x-custom-header', 'value')
  return response
}

export const config = {
  matcher: '/api/:path*',
}
注意:
middleware.ts
在Next.js 16中仍可运行,但已被废弃。为了未来兼容性,请迁移到
proxy.ts
参考模板:
templates/proxy-migration.ts
参考文档:
references/proxy-vs-middleware.md

3. Parallel Routes Require
default.js
(BREAKING)

3. 并行路由必须包含
default.js
(破坏性变更)

Breaking Change: Parallel routes now require explicit
default.js
files. Without them, routes will fail during soft navigation.
Structure:
app/
├── @auth/
│   ├── login/
│   │   └── page.tsx
│   └── default.tsx    ← REQUIRED in Next.js 16
├── @dashboard/
│   ├── overview/
│   │   └── page.tsx
│   └── default.tsx    ← REQUIRED in Next.js 16
└── layout.tsx
Layout:
typescript
// app/layout.tsx
export default function Layout({
  children,
  auth,
  dashboard,
}: {
  children: React.ReactNode
  auth: React.ReactNode
  dashboard: React.ReactNode
}) {
  return (
    <html>
      <body>
        {auth}
        {dashboard}
        {children}
      </body>
    </html>
  )
}
Default Fallback (REQUIRED):
typescript
// app/@auth/default.tsx
export default function AuthDefault() {
  return null // or <Skeleton /> or redirect
}

// app/@dashboard/default.tsx
export default function DashboardDefault() {
  return null
}
Why Required: Next.js 16 changed how parallel routes handle soft navigation. Without
default.js
, unmatched slots will error during client-side navigation.
See Template:
templates/parallel-routes-with-default.tsx

变更内容: 并行路由现在必须显式包含
default.js
文件。如果没有该文件,软导航过程中路由会失败。
目录结构:
app/
├── @auth/
│   ├── login/
│   │   └── page.tsx
│   └── default.tsx    ← Next.js 16中为必填项
├── @dashboard/
│   ├── overview/
│   │   └── page.tsx
│   └── default.tsx    ← Next.js 16中为必填项
└── layout.tsx
布局文件写法:
typescript
// app/layout.tsx
export default function Layout({
  children,
  auth,
  dashboard,
}: {
  children: React.ReactNode
  auth: React.ReactNode
  dashboard: React.ReactNode
}) {
  return (
    <html>
      <body>
        {auth}
        {dashboard}
        {children}
      </body>
    </html>
  )
}
默认回退组件(必填):
typescript
// app/@auth/default.tsx
export default function AuthDefault() {
  return null // 或<Skeleton />或重定向
}

// app/@dashboard/default.tsx
export default function DashboardDefault() {
  return null
}
必填原因: Next.js 16改变了并行路由处理软导航的方式。如果没有
default.js
,客户端导航过程中未匹配的插槽会报错。
参考模板:
templates/parallel-routes-with-default.tsx

4. Removed Features (BREAKING)

4. 已移除的特性(破坏性变更)

The following features are REMOVED in Next.js 16:
  1. AMP Support - Entirely removed. Migrate to standard pages.
  2. next lint
    command
    - Use ESLint or Biome directly.
  3. serverRuntimeConfig
    and
    publicRuntimeConfig
    - Use environment variables instead.
  4. experimental.ppr
    flag
    - Evolved into Cache Components. Use
    "use cache"
    directive.
  5. Automatic
    scroll-behavior: smooth
    - Add manually if needed.
  6. Node.js 18 support - Minimum version is now 20.9+.
Migration:
  • AMP: Convert AMP pages to standard pages or use separate AMP implementation.
  • Linting: Run
    npx eslint .
    or
    npx biome lint .
    directly.
  • Config: Replace
    serverRuntimeConfig
    with
    process.env.VARIABLE
    .
  • PPR: Migrate from
    experimental.ppr
    to
    "use cache"
    directive (see Cache Components section).

以下特性在Next.js 16中已被移除:
  1. AMP支持 - 完全移除,请迁移到标准页面。
  2. next lint
    命令
    - 请直接使用ESLint或Biome。
  3. serverRuntimeConfig
    publicRuntimeConfig
    - 请使用环境变量替代。
  4. experimental.ppr
    标志
    - 已演变为缓存组件,请使用
    "use cache"
    指令。
  5. 自动
    scroll-behavior: smooth
    - 如需此功能请手动添加。
  6. Node.js 18支持 - 最低要求版本现在为20.9+
迁移方法:
  • AMP: 将AMP页面转换为标准页面,或使用独立的AMP实现方案。
  • 代码检查: 直接运行
    npx eslint .
    npx biome lint .
  • 配置: 使用
    process.env.VARIABLE
    替代
    serverRuntimeConfig
  • PPR: 从
    experimental.ppr
    迁移到
    "use cache"
    指令(请查看缓存组件章节)。

5. Version Requirements (BREAKING)

5. 版本要求(破坏性变更)

Next.js 16 requires:
  • Node.js: 20.9+ (Node.js 18 no longer supported)
  • TypeScript: 5.1+ (if using TypeScript)
  • React: 19.2+ (automatically installed with Next.js 16)
  • Browsers: Chrome 111+, Safari 16.4+, Firefox 109+, Edge 111+
Check Versions:
bash
node --version    # Should be 20.9+
npm --version     # Should be 10+
npx next --version # Should be 16.0.0+
Upgrade Node.js:
bash
undefined
Next.js 16要求:
  • Node.js: 20.9+(不再支持Node.js 18)
  • TypeScript: 5.1+(如果使用TypeScript)
  • React: 19.2+(安装Next.js 16时会自动安装)
  • 浏览器: Chrome 111+、Safari 16.4+、Firefox 109+、Edge 111+
检查版本:
bash
node --version    # 应显示20.9+
npm --version     # 应显示10+
npx next --version # 应显示16.0.0+
升级Node.js:
bash
undefined

Using nvm

使用nvm

nvm install 20 nvm use 20 nvm alias default 20
nvm install 20 nvm use 20 nvm alias default 20

Using Homebrew (macOS)

使用Homebrew(macOS)

brew install node@20
brew install node@20

Using apt (Ubuntu/Debian)

使用apt(Ubuntu/Debian)

sudo apt update sudo apt install nodejs npm

---
sudo apt update sudo apt install nodejs npm

---

6. Image Defaults Changed (BREAKING)

6. Image默认配置变更(破坏性变更)

Next.js 16 changed
next/image
defaults
:
SettingNext.js 15Next.js 16
TTL (cache duration)60 seconds4 hours
imageSizes
[16, 32, 48, 64, 96, 128, 256, 384]
[640, 750, 828, 1080, 1200]
(reduced)
qualities
[75, 90, 100]
[75]
(single quality)
Impact:
  • Images cache longer (4 hours vs 60 seconds)
  • Fewer image sizes generated (smaller builds, but less granular)
  • Single quality (75) generated instead of multiple
Override Defaults (if needed):
typescript
// next.config.ts
import type { NextConfig } from 'next'

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

export default config
See Template:
templates/image-optimization.tsx

Next.js 16修改了
next/image
的默认配置
:
设置项Next.js 15Next.js 16
TTL(缓存时长)60秒4小时
imageSizes
[16, 32, 48, 64, 96, 128, 256, 384]
[640, 750, 828, 1080, 1200]
(已缩减)
qualities
[75, 90, 100]
[75]
(仅单一质量)
影响:
  • 图片缓存时长更长(4小时 vs 60秒)
  • 生成的图片尺寸更少(构建包更小,但粒度更粗)
  • 仅生成单一质量(75)的图片,而非多种质量
如需覆盖默认配置:
typescript
// next.config.ts
import type { NextConfig } from 'next'

const config: NextConfig = {
  images: {
    minimumCacheTTL: 60, // 恢复为60秒
    deviceSizes: [640, 750, 828, 1080, 1200, 1920], // 添加更大尺寸
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], // 恢复旧尺寸
    formats: ['image/webp'], // 默认值
  },
}

export default config
参考模板:
templates/image-optimization.tsx

Cache Components & Caching APIs

缓存组件与缓存API

NEW in Next.js 16: Cache Components introduce opt-in caching with the
"use cache"
directive, replacing implicit caching from Next.js 15.
Next.js 16新增特性: 缓存组件通过
"use cache"
指令实现可选缓存,替代了Next.js 15中的隐式缓存。

1. Overview

1. 概述

What Changed:
  • Next.js 15: Implicit caching (all Server Components cached by default)
  • Next.js 16: Opt-in caching with
    "use cache"
    directive
Why the Change: Explicit caching gives developers more control and makes caching behavior predictable.
Important Caching Defaults (Community-sourced):
FeatureNext.js 14Next.js 15/16
fetch() requestsCached by defaultNOT cached by default
Router Cache (dynamic pages)Cached on clientNOT cached by default
Router Cache (static pages)CachedStill cached
Route Handlers (GET)CachedDynamic by default
Best Practice: Default to dynamic in Next.js 16. Start with no caching and add it where beneficial, rather than debugging unexpected cache hits. Always test with production builds - the development server behaves differently.
Cache Components enable:
  • Component-level caching (cache specific components, not entire pages)
  • Function-level caching (cache expensive computations)
  • Page-level caching (cache entire pages selectively)
  • Partial Prerendering (PPR) - Cache static parts, render dynamic parts on-demand

变更内容:
  • Next.js 15: 隐式缓存(所有Server Components默认被缓存)
  • Next.js 16: 通过
    "use cache"
    指令实现可选缓存
变更原因: 显式缓存让开发者拥有更多控制权,同时让缓存行为更可预测。
重要缓存默认值(社区收集):
特性Next.js 14Next.js 15/16
fetch()请求默认缓存默认不缓存
路由缓存(动态页面)在客户端缓存默认不缓存
路由缓存(静态页面)缓存仍然缓存
路由处理器(GET)缓存默认动态
最佳实践: 在Next.js 16中默认使用动态模式。从无缓存开始,在需要的地方添加缓存,而不是调试意外的缓存命中。始终使用生产构建进行测试——开发服务器的行为与生产环境不同。
缓存组件支持:
  • 组件级缓存(仅缓存特定组件,而非整个页面)
  • 函数级缓存(缓存昂贵的计算)
  • 页面级缓存(选择性缓存整个页面)
  • 部分预渲染(PPR) - 缓存静态部分,按需渲染动态部分

2.
"use cache"
Directive

2.
"use cache"
指令

Syntax: Add
"use cache"
at the top of a Server Component, function, or route handler.
Component-level caching:
typescript
// app/components/expensive-component.tsx
'use cache'

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

  return (
    <div>
      <h1>{json.title}</h1>
      <p>{json.description}</p>
    </div>
  )
}
Function-level caching:
typescript
// lib/data.ts
'use cache'

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

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

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

  return <div>{product.name}</div>
}
Page-level caching:
typescript
// app/blog/[slug]/page.tsx
'use cache'

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

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

  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  )
}
See Template:
templates/cache-component-use-cache.tsx

语法: 在Server Component、函数或路由处理器的顶部添加
"use cache"
组件级缓存:
typescript
// app/components/expensive-component.tsx
'use cache'

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

  return (
    <div>
      <h1>{json.title}</h1>
      <p>{json.description}</p>
    </div>
  )
}
函数级缓存:
typescript
// lib/data.ts
'use cache'

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

// 组件中的用法
import { getExpensiveData } from '@/lib/data'

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

  return <div>{product.name}</div>
}
页面级缓存:
typescript
// app/blog/[slug]/page.tsx
'use cache'

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

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

  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  )
}
参考模板:
templates/cache-component-use-cache.tsx

3. Partial Prerendering (PPR)

3. 部分预渲染(PPR)

PPR allows caching static parts of a page while rendering dynamic parts on-demand.
Pattern:
typescript
// app/dashboard/page.tsx

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

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

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

// Page combines both
export default function Dashboard() {
  return (
    <div>
      <StaticHeader /> {/* Cached */}
      <DynamicUserInfo /> {/* Dynamic */}
    </div>
  )
}
When to Use PPR:
  • Page has both static and dynamic content
  • Want to cache layout/header/footer but render user-specific content
  • Need fast initial load (static parts) + personalization (dynamic parts)
See Reference:
references/cache-components-guide.md

PPR允许缓存页面的静态部分,同时按需渲染动态部分。
实践模式:
typescript
// app/dashboard/page.tsx

// 静态头部(已缓存)
'use cache'
async function StaticHeader() {
  return <header>My App</header>
}

// 动态用户信息(未缓存)
async function DynamicUserInfo() {
  const cookieStore = await cookies()
  const userId = cookieStore.get('userId')?.value
  const user = await fetch(`/api/users/${userId}`).then(r => r.json())

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

// 页面组合两者
export default function Dashboard() {
  return (
    <div>
      <StaticHeader /> {/* 已缓存 */}
      <DynamicUserInfo /> {/* 动态 */}
    </div>
  )
}
PPR适用场景:
  • 页面同时包含静态和动态内容
  • 希望缓存布局/头部/页脚,但渲染用户专属内容
  • 需要快速初始加载(静态部分)+ 个性化(动态部分)
参考文档:
references/cache-components-guide.md

4.
revalidateTag()
- Updated API

4.
revalidateTag()
- 更新后的API

BREAKING CHANGE:
revalidateTag()
now requires a second argument (
cacheLife
profile) for stale-while-revalidate behavior.
Before (Next.js 15):
typescript
import { revalidateTag } from 'next/cache'

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

export async function updatePost(id: string) {
  await fetch(`/api/posts/${id}`, { method: 'PATCH' })
  revalidateTag('posts', 'max') // ✅ Second argument required in Next.js 16
}
Built-in Cache Life Profiles:
  • 'max'
    - Maximum staleness (recommended for most use cases)
  • 'hours'
    - Stale after hours
  • 'days'
    - Stale after days
  • 'weeks'
    - Stale after weeks
  • 'default'
    - Default cache behavior
Custom Cache Life Profile:
typescript
revalidateTag('posts', {
  stale: 3600, // Stale after 1 hour (seconds)
  revalidate: 86400, // Revalidate every 24 hours (seconds)
  expire: false, // Never expire (optional)
})
Pattern in Server Actions:
typescript
'use server'

import { revalidateTag } from 'next/cache'

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

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

  revalidateTag('posts', 'max') // ✅ Revalidate with max staleness
}
See Template:
templates/revalidate-tag-cache-life.ts

破坏性变更:
revalidateTag()
现在需要第二个参数
cacheLife
配置文件)以实现stale-while-revalidate行为。
Next.js 15中的写法:
typescript
import { revalidateTag } from 'next/cache'

export async function updatePost(id: string) {
  await fetch(`/api/posts/${id}`, { method: 'PATCH' })
  revalidateTag('posts') // ❌ Next.js 15中仅需一个参数
}
Next.js 16中的正确写法:
typescript
import { revalidateTag } from 'next/cache'

export async function updatePost(id: string) {
  await fetch(`/api/posts/${id}`, { method: 'PATCH' })
  revalidateTag('posts', 'max') // ✅ Next.js 16中需要第二个参数
}
内置缓存生命周期配置:
  • 'max'
    - 最大过期时长(推荐用于大多数场景)
  • 'hours'
    - 数小时后过期
  • 'days'
    - 数天后过期
  • 'weeks'
    - 数周后过期
  • 'default'
    - 默认缓存行为
自定义缓存生命周期配置:
typescript
revalidateTag('posts', {
  stale: 3600, // 1小时后过期(秒)
  revalidate: 86400, // 每24小时重新验证(秒)
  expire: false, // 永不过期(可选)
})
Server Actions中的实践模式:
typescript
'use server'

import { revalidateTag } from 'next/cache'

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

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

  revalidateTag('posts', 'max') // ✅ 使用最大过期时长重新验证
}
参考模板:
templates/revalidate-tag-cache-life.ts

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

5.
updateTag()
- 新增API(仅适用于Server Actions)

NEW in Next.js 16:
updateTag()
provides read-your-writes semantics for Server Actions.
What it does:
  • Expires cache immediately
  • Refreshes data within the same request
  • Shows updated data right after mutation (no stale data)
Difference from
revalidateTag()
:
  • revalidateTag()
    : Stale-while-revalidate (shows stale data, revalidates in background)
  • updateTag()
    : Immediate refresh (expires cache, fetches fresh data in same request)
Use Case: Forms, user settings, or any mutation where user expects immediate feedback.
Pattern:
typescript
'use server'

import { updateTag } from 'next/cache'

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

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

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

  // User sees updated data immediately (no stale data)
}
When to Use:
  • updateTag()
    : User settings, profile updates, critical mutations (immediate feedback)
  • revalidateTag()
    : Blog posts, product listings, non-critical updates (background revalidation)
See Template:
templates/server-action-update-tag.ts

Next.js 16新增特性:
updateTag()
为Server Actions提供读写一致性语义
功能:
  • 立即过期缓存
  • 在同一请求中刷新数据
  • 突变后立即显示更新后的数据(无 stale 数据)
revalidateTag()
的区别
:
  • revalidateTag()
    : Stale-while-revalidate(显示旧数据,在后台重新验证)
  • updateTag()
    : 立即刷新(过期缓存,在同一请求中获取新数据)
适用场景: 表单、用户设置或任何用户期望立即获得反馈的突变操作。
实践模式:
typescript
'use server'

import { updateTag } from 'next/cache'

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

  // 更新数据库
  await db.users.update({ name, email })

  // 立即刷新缓存(读写一致性)
  updateTag('user-profile')

  // 用户立即看到更新后的数据(无 stale 数据)
}
使用时机:
  • updateTag()
    : 用户设置、个人资料更新、关键突变操作(需要立即反馈)
  • revalidateTag()
    : 博客文章、产品列表、非关键更新(后台重新验证)
参考模板:
templates/server-action-update-tag.ts

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

6.
refresh()
- 新增API(仅适用于Server Actions)

NEW in Next.js 16:
refresh()
refreshes uncached data only (complements client-side
router.refresh()
).
When to Use:
  • Refresh dynamic data without affecting cached data
  • Complement
    router.refresh()
    on server side
Pattern:
typescript
'use server'

import { refresh } from 'next/cache'

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

  // Cached data (e.g., static header) remains cached
}
Difference from
revalidateTag()
and
updateTag()
:
  • refresh()
    : Only refreshes uncached data
  • revalidateTag()
    : Revalidates specific tagged data (stale-while-revalidate)
  • updateTag()
    : Immediately expires and refreshes specific tagged data
See Reference:
references/cache-components-guide.md


Next.js 16新增特性:
refresh()
仅刷新未缓存的数据(作为客户端
router.refresh()
的补充)。
适用场景:
  • 刷新动态数据而不影响缓存数据
  • 在服务器端补充
    router.refresh()
    的功能
实践模式:
typescript
'use server'

import { refresh } from 'next/cache'

export async function refreshDashboard() {
  // 刷新未缓存数据(如实时指标)
  refresh()

  // 缓存数据(如静态头部)保持缓存状态
}
revalidateTag()
updateTag()
的区别
:
  • refresh()
    : 仅刷新未缓存的数据
  • revalidateTag()
    : 重新验证特定标签的数据(Stale-while-revalidate)
  • updateTag()
    : 立即过期并刷新特定标签的数据
参考文档:
references/cache-components-guide.md


Route Handlers (Next.js 16 Updates)

路由处理器(Next.js 16更新)

Async Params in Route Handlers (BREAKING)

路由处理器中的异步参数(破坏性变更)

IMPORTANT:
params
and
headers()
are now async in Next.js 16 route handlers.
Example:
typescript
// app/api/posts/[id]/route.ts
import { NextResponse } from 'next/server'
import { headers } from 'next/headers'

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

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

  return NextResponse.json(post)
}
See Template:
templates/route-handler-api.ts

重要提示: Next.js 16中路由处理器的
params
headers()
现在为异步。
示例:
typescript
// app/api/posts/[id]/route.ts
import { NextResponse } from 'next/server'
import { headers } from 'next/headers'

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

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

  return NextResponse.json(post)
}
参考模板:
templates/route-handler-api.ts

Proxy vs Middleware

Proxy vs Middleware

Next.js 16 introduces
proxy.ts
to replace
middleware.ts
.
**Next.js 16引入了
proxy.ts
**以替代
middleware.ts

Why the Change?

变更原因

  • middleware.ts
    : Runs on Edge runtime (limited Node.js APIs)
  • proxy.ts
    : Runs on Node.js runtime (full Node.js APIs)
The new
proxy.ts
makes the network boundary explicit and provides more flexibility.
  • middleware.ts
    : 在Edge运行时运行(Node.js API受限)
  • proxy.ts
    : 在Node.js运行时运行(完整Node.js API支持)
新的
proxy.ts
明确了网络边界,同时提供了更大的灵活性。

Migration

迁移方法

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

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

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

  return NextResponse.next()
}

export const config = {
  matcher: '/dashboard/:path*',
}
After (proxy.ts):
typescript
// proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

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

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

  return NextResponse.next()
}

export const config = {
  matcher: '/dashboard/:path*',
}
See Template:
templates/proxy-migration.ts
See Reference:
references/proxy-vs-middleware.md

旧写法(middleware.ts):
typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // 检查认证
  const token = request.cookies.get('token')

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

  return NextResponse.next()
}

export const config = {
  matcher: '/dashboard/:path*',
}
新写法(proxy.ts):
typescript
// proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function proxy(request: NextRequest) {
  // 检查认证
  const token = request.cookies.get('token')

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

  return NextResponse.next()
}

export const config = {
  matcher: '/dashboard/:path*',
}
参考模板:
templates/proxy-migration.ts
参考文档:
references/proxy-vs-middleware.md

Parallel Routes - default.js Required (BREAKING)

并行路由 - default.js 必填(破坏性变更)

Breaking Change in Next.js 16: Parallel routes now require explicit
default.js
files.
Structure:
app/
├── @modal/
│   ├── login/page.tsx
│   └── default.tsx  ← REQUIRED in Next.js 16
├── @feed/
│   ├── trending/page.tsx
│   └── default.tsx  ← REQUIRED in Next.js 16
└── layout.tsx
Default Files (REQUIRED):
typescript
// app/@modal/default.tsx
export default function ModalDefault() {
  return null // or <Skeleton /> or redirect
}
Why Required: Next.js 16 changed soft navigation handling. Without
default.js
, unmatched slots error during client-side navigation.
Advanced Edge Case (Community-sourced): Even WITH
default.js
files, hard navigating or refreshing routes with parallel routes can return 404 errors. The workaround is adding a catch-all route.
Workaround:
typescript
// app/@modal/[...catchAll]/page.tsx
export default function CatchAll() {
  return null;
}

// OR use catch-all in default.tsx
// app/@modal/default.tsx
export default function ModalDefault({ params }: { params: { catchAll?: string[] } }) {
  return null; // Handles all unmatched routes
}
See Template:
templates/parallel-routes-with-default.tsx

Next.js 16中的破坏性变更: 并行路由现在必须包含显式的
default.js
文件。
目录结构:
app/
├── @modal/
│   ├── login/page.tsx
│   └── default.tsx  ← Next.js 16中为必填项
├── @feed/
│   ├── trending/page.tsx
│   └── default.tsx  ← Next.js 16中为必填项
└── layout.tsx
默认文件(必填):
typescript
// app/@modal/default.tsx
export default function ModalDefault() {
  return null // 或<Skeleton />或重定向
}
必填原因: Next.js 16改变了软导航的处理方式。如果没有
default.js
,客户端导航过程中未匹配的插槽会报错。
高级边缘情况(社区收集): 即使有
default.js
文件,硬导航或刷新包含并行路由的页面仍可能返回404错误。解决方法是添加一个catch-all路由。
解决方法:
typescript
// app/@modal/[...catchAll]/page.tsx
export default function CatchAll() {
  return null;
}

// 或在default.tsx中使用catch-all
// app/@modal/default.tsx
export default function ModalDefault({ params }: { params: { catchAll?: string[] } }) {
  return null; // 处理所有未匹配的路由
}
参考模板:
templates/parallel-routes-with-default.tsx

React 19.2 Features

React 19.2 特性

Next.js 16 integrates React 19.2, which includes new features from React Canary.
Next.js 16集成了React 19.2,其中包含了React Canary中的新特性。

1. View Transitions

1. View Transitions

Use Case: Smooth animations between page transitions.
typescript
'use client'

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

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

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

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

  return <a href={href} onClick={handleClick}>{children}</a>
}
With CSS View Transitions API:
css
/* app/globals.css */
@view-transition {
  navigation: auto;
}

/* Animate elements with view-transition-name */
.page-title {
  view-transition-name: page-title;
}
See Template:
templates/view-transitions-react-19.tsx

适用场景: 页面切换时的平滑动画。
typescript
'use client'

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

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

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

    // 将导航包裹在startTransition中以实现View Transitions
    startTransition(() => {
      router.push(href)
    })
  }

  return <a href={href} onClick={handleClick}>{children}</a>
}
结合CSS View Transitions API:
css
/* app/globals.css */
@view-transition {
  navigation: auto;
}

/* 使用view-transition-name为元素添加动画 */
.page-title {
  view-transition-name: page-title;
}
参考模板:
templates/view-transitions-react-19.tsx

2.
useEffectEvent()
(Experimental)

2.
useEffectEvent()
(实验性)

Use Case: Extract non-reactive logic from
useEffect
.
typescript
'use client'

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

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

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

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

  return <div>Chat Room {roomId}</div>
}
Why Use It: Prevents unnecessary
useEffect
re-runs when callback dependencies change.

适用场景: 从
useEffect
中提取非响应式逻辑。
typescript
'use client'

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

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

  useEffect(() => {
    const connection = connectToRoom(roomId)
    onConnected() // 非响应式回调

    return () => connection.disconnect()
  }, [roomId]) // 仅当roomId变化时重新运行

  return <div>Chat Room {roomId}</div>
}
使用原因: 当回调依赖项变化时,避免不必要的
useEffect
重新运行。

3. React Compiler (Stable)

3. React Compiler(已稳定)

Use Case: Automatic memoization without
useMemo
,
useCallback
.
Enable in next.config.ts:
typescript
import type { NextConfig } from 'next'

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

export default config
Install Plugin:
bash
npm install babel-plugin-react-compiler
Example (no manual memoization needed):
typescript
'use client'

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

  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  )
}
See Reference:
references/react-19-integration.md

适用场景: 无需
useMemo
useCallback
即可实现自动记忆化。
在next.config.ts中启用:
typescript
import type { NextConfig } from 'next'

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

export default config
安装插件:
bash
npm install babel-plugin-react-compiler
示例(无需手动记忆化):
typescript
'use client'

export function ExpensiveList({ items }: { items: string[] }) {
  // React Compiler自动记忆化
  const filteredItems = items.filter(item => item.length > 3)

  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  )
}
参考文档:
references/react-19-integration.md

Turbopack (Stable in Next.js 16)

Turbopack(Next.js 16中已稳定)

NEW: Turbopack is now the default bundler in Next.js 16.
Performance Improvements:
  • 2–5× faster production builds
  • Up to 10× faster Fast Refresh
Opt-out (if needed):
bash
npm run build -- --webpack
Enable File System Caching (experimental):
typescript
// next.config.ts
import type { NextConfig } from 'next'

const config: NextConfig = {
  experimental: {
    turbopack: {
      fileSystemCaching: true, // Beta: Persist cache between runs
    },
  },
}

export default config

新增特性: Turbopack现在是Next.js 16中的默认打包工具
性能提升:
  • 生产构建速度提升2–5倍
  • Fast Refresh速度提升最高10倍
如需禁用(如果需要):
bash
npm run build -- --webpack
启用文件系统缓存(实验性):
typescript
// next.config.ts
import type { NextConfig } from 'next'

const config: NextConfig = {
  experimental: {
    turbopack: {
      fileSystemCaching: true, // Beta版本:在多次运行之间持久化缓存
    },
  },
}

export default config

Turbopack Production Limitations (as of Next.js 16.1)

Turbopack生产环境局限性(截至Next.js 16.1)

Known Issues:
已知问题:

1. Prisma Incompatibility

1. Prisma兼容性问题

Turbopack production builds fail with Prisma ORM (v6.5.0+). Error: "The 'path' argument must be of type string."
Workaround:
bash
undefined
Turbopack生产构建与Prisma ORM(v6.5.0+)不兼容。错误信息:"The 'path' argument must be of type string."
解决方法:
bash
// 使用webpack进行生产构建
npm run build -- --webpack
或在
next.config.ts
中配置:
typescript
const config: NextConfig = {
  experimental: {
    turbo: false, // 禁用生产环境中的Turbopack
  },
};

Use webpack for production builds

2. Source Maps安全风险

npm run build -- --webpack

Or in `next.config.ts`:
```typescript
const config: NextConfig = {
  experimental: {
    turbo: false, // Disable Turbopack for production
  },
};

Turbopack目前始终为浏览器构建生产环境Source Maps,这会在生产部署中暴露源代码。
解决方法:
typescript
// next.config.ts
const config: NextConfig = {
  productionBrowserSourceMaps: false, // 禁用Source Maps
};
或在部署中排除
.map
文件:
bash
undefined

2. Source Maps Security Risk

.vercelignore或类似配置文件

Turbopack currently always builds production source maps for the browser, exposing source code in production deployments.
Workaround:
typescript
// next.config.ts
const config: NextConfig = {
  productionBrowserSourceMaps: false, // Disable source maps
};
Or exclude
.map
files in deployment:
bash
undefined
*.map

---

.vercelignore or similar

3. 外部模块哈希不匹配(Monorepos)

*.map

---
node_modules
结构不同时(pnpm、yarn workspaces、Monorepos),Turbopack生成的外部模块引用哈希会不匹配。这会导致生产构建中出现"Module not found"错误。
症状:
  • 本地构建成功,但CI/CD中构建失败
  • 打包后的引用哈希与实际模块文件不匹配
解决方法:
typescript
// next.config.ts
const config: NextConfig = {
  experimental: {
    serverExternalPackages: ['package-name'], // 显式标记外部包
  },
};

3. External Module Hash Mismatches (Monorepos)

4. 包大小差异(社区收集)

Turbopack generates external module references with hashes that don't match when
node_modules
structure differs (pnpm, yarn workspaces, monorepos). This causes "Module not found" errors in production builds.
Symptoms:
  • Build succeeds locally but fails in CI/CD
  • Hash mismatches between bundled references and actual module files
Workaround:
typescript
// next.config.ts
const config: NextConfig = {
  experimental: {
    serverExternalPackages: ['package-name'], // Explicitly externalize packages
  },
};

使用Turbopack构建的包大小可能与webpack构建的不同。这是正常现象,随着Turbopack的成熟,该情况会逐步优化。

4. Bundle Size Differences (Community-sourced)

常见错误与解决方案

1. 错误:
params
is a Promise

Bundle sizes built with Turbopack may differ from webpack builds. This is expected and being optimized as Turbopack matures.

错误信息:
Type 'Promise<{ id: string }>' is not assignable to type '{ id: string }'
原因: Next.js 16将
params
改为异步。
解决方法: Await
params
:
typescript
// ❌ 旧写法
export default function Page({ params }: { params: { id: string } }) {
  const id = params.id
}

// ✅ 新写法
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
}

Common Errors & Solutions

2. 错误:
searchParams
is a Promise

1. Error:
params
is a Promise

Error:
Type 'Promise<{ id: string }>' is not assignable to type '{ id: string }'
Cause: Next.js 16 changed
params
to async.
Solution: Await
params
:
typescript
// ❌ Before
export default function Page({ params }: { params: { id: string } }) {
  const id = params.id
}

// ✅ After
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
}

错误信息:
Property 'query' does not exist on type 'Promise<{ query: string }>'
原因:
searchParams
在Next.js 16中现在为异步。
解决方法:
typescript
// ❌ 旧写法
export default function Page({ searchParams }: { searchParams: { query: string } }) {
  const query = searchParams.query
}

// ✅ 新写法
export default async function Page({ searchParams }: { searchParams: Promise<{ query: string }> }) {
  const { query } = await searchParams
}

2. Error:
searchParams
is a Promise

3. 错误:
cookies()
requires await

Error:
Property 'query' does not exist on type 'Promise<{ query: string }>'
Cause:
searchParams
is now async in Next.js 16.
Solution:
typescript
// ❌ Before
export default function Page({ searchParams }: { searchParams: { query: string } }) {
  const query = searchParams.query
}

// ✅ After
export default async function Page({ searchParams }: { searchParams: Promise<{ query: string }> }) {
  const { query } = await searchParams
}

错误信息:
'cookies' implicitly has return type 'any'
原因:
cookies()
在Next.js 16中现在为异步。
解决方法:
typescript
// ❌ 旧写法
import { cookies } from 'next/headers'

export function MyComponent() {
  const cookieStore = cookies()
}

// ✅ 新写法
import { cookies } from 'next/headers'

export async function MyComponent() {
  const cookieStore = await cookies()
}

3. Error:
cookies()
requires await

4. 错误: Parallel route missing
default.js

Error:
'cookies' implicitly has return type 'any'
Cause:
cookies()
is now async in Next.js 16.
Solution:
typescript
// ❌ Before
import { cookies } from 'next/headers'

export function MyComponent() {
  const cookieStore = cookies()
}

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

export async function MyComponent() {
  const cookieStore = await cookies()
}

错误信息:
Error: Parallel route @modal/login was matched but no default.js was found
原因: Next.js 16要求所有并行路由都要有
default.js
解决方法: 添加
default.tsx
文件:
typescript
// app/@modal/default.tsx
export default function ModalDefault() {
  return null
}

4. Error: Parallel route missing
default.js

5. 错误:
revalidateTag()
requires 2 arguments

Error:
Error: Parallel route @modal/login was matched but no default.js was found
Cause: Next.js 16 requires
default.js
for all parallel routes.
Solution: Add
default.tsx
files:
typescript
// app/@modal/default.tsx
export default function ModalDefault() {
  return null
}

错误信息:
Expected 2 arguments, but got 1
原因: Next.js 16中
revalidateTag()
需要一个
cacheLife
参数。
解决方法:
typescript
// ❌ 旧写法
revalidateTag('posts')

// ✅ 新写法
revalidateTag('posts', 'max')

5. Error:
revalidateTag()
requires 2 arguments

6. 错误: Cannot use React hooks in Server Component

Error:
Expected 2 arguments, but got 1
Cause:
revalidateTag()
now requires a
cacheLife
argument in Next.js 16.
Solution:
typescript
// ❌ Before
revalidateTag('posts')

// ✅ After
revalidateTag('posts', 'max')

错误信息:
You're importing a component that needs useState. It only works in a Client Component
原因: 在Server Component中使用了React钩子。
解决方法: 添加
'use client'
指令:
typescript
// ✅ 在顶部添加'use client'
'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

6. Error: Cannot use React hooks in Server Component

7. 错误:
middleware.ts
is deprecated

Error:
You're importing a component that needs useState. It only works in a Client Component
Cause: Using React hooks in Server Component.
Solution: Add
'use client'
directive:
typescript
// ✅ Add 'use client' at the top
'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

警告信息:
Warning: middleware.ts is deprecated. Use proxy.ts instead.
解决方法: 迁移到
proxy.ts
:
typescript
// 重命名:middleware.ts → proxy.ts
// 重命名函数:middleware → proxy

export function proxy(request: NextRequest) {
  // 相同逻辑
}

7. Error:
middleware.ts
is deprecated

8. 错误: Turbopack build failure

Warning:
Warning: middleware.ts is deprecated. Use proxy.ts instead.
Solution: Migrate to
proxy.ts
:
typescript
// Rename: middleware.ts → proxy.ts
// Rename function: middleware → proxy

export function proxy(request: NextRequest) {
  // Same logic
}

错误信息:
Error: Failed to compile with Turbopack
原因: Turbopack现在是Next.js 16中的默认选项。
解决方法: 如果不兼容,禁用Turbopack:
bash
npm run build -- --webpack

8. Error: Turbopack build failure

9. 错误: Invalid
next/image
src

Error:
Error: Failed to compile with Turbopack
Cause: Turbopack is now default in Next.js 16.
Solution: Opt out of Turbopack if incompatible:
bash
npm run build -- --webpack

错误信息:
Invalid src prop (https://example.com/image.jpg) on `next/image`. Hostname "example.com" is not configured under images in your `next.config.js`
解决方法: 在
next.config.ts
中添加远程模式:
typescript
const config: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
      },
    ],
  },
}

9. Error: Invalid
next/image
src

10. 错误: Cannot import Server Component into Client Component

Error:
Invalid src prop (https://example.com/image.jpg) on `next/image`. Hostname "example.com" is not configured under images in your `next.config.js`
Solution: Add remote patterns in
next.config.ts
:
typescript
const config: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
      },
    ],
  },
}

错误信息:
You're importing a Server Component into a Client Component
解决方法: 将Server Component作为子组件传递:
typescript
// ❌ 错误写法
'use client'
import { ServerComponent } from './server-component' // 错误

export function ClientComponent() {
  return <ServerComponent />
}

// ✅ 正确写法
'use client'

export function ClientComponent({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

// 用法
<ClientComponent>
  <ServerComponent /> {/* 作为子组件传递 */}
</ClientComponent>

10. Error: Cannot import Server Component into Client Component

11. 错误:
generateStaticParams
not working

Error:
You're importing a Server Component into a Client Component
Solution: Pass Server Component as children:
typescript
// ❌ Wrong
'use client'
import { ServerComponent } from './server-component' // Error

export function ClientComponent() {
  return <ServerComponent />
}

// ✅ Correct
'use client'

export function ClientComponent({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

// Usage
<ClientComponent>
  <ServerComponent /> {/* Pass as children */}
</ClientComponent>

原因:
generateStaticParams
仅在静态生成模式下生效(
export const dynamic = 'force-static'
)。
解决方法:
typescript
export const dynamic = 'force-static'

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

11. Error:
generateStaticParams
not working

12. 错误:
fetch()
not caching

Cause:
generateStaticParams
only works with static generation (
export const dynamic = 'force-static'
).
Solution:
typescript
export const dynamic = 'force-static'

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

原因: Next.js 16使用
"use cache"
指令实现可选缓存。
解决方法: 在组件或函数中添加
"use cache"
:
typescript
'use cache'

export async function getPosts() {
  const response = await fetch('/api/posts')
  return response.json()
}

12. Error:
fetch()
not caching

13. 错误: Route collision with Route Groups

Cause: Next.js 16 uses opt-in caching with
"use cache"
directive.
Solution: Add
"use cache"
to component or function:
typescript
'use cache'

export async function getPosts() {
  const response = await fetch('/api/posts')
  return response.json()
}

错误信息:
Error: Conflicting routes: /about and /(marketing)/about
原因: 路由组创建了相同的URL路径。
解决方法: 确保路由组不冲突:
app/
├── (marketing)/about/page.tsx  → /about
└── (shop)/about/page.tsx       → 错误:重复的/about

13. Error: Route collision with Route Groups

修复:使用不同的路由

Error:
Error: Conflicting routes: /about and /(marketing)/about
Cause: Route groups create same URL path.
Solution: Ensure route groups don't conflict:
app/
├── (marketing)/about/page.tsx  → /about
└── (shop)/about/page.tsx       → ERROR: Duplicate /about
app/ ├── (marketing)/about/page.tsx → /about └── (shop)/store-info/page.tsx → /store-info

---

Fix: Use different routes

14. 错误: Metadata not updating

app/ ├── (marketing)/about/page.tsx → /about └── (shop)/store-info/page.tsx → /store-info

---
原因: 在动态页面中使用了动态元数据但未使用
generateMetadata()
解决方法: 对动态页面使用
generateMetadata()
:
typescript
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
  const { id } = await params
  const post = await fetch(`/api/posts/${id}`).then(r => r.json())

  return {
    title: post.title,
    description: post.excerpt,
  }
}

14. Error: Metadata not updating

15. 错误:
next/font
font not loading

Cause: Using dynamic metadata without
generateMetadata()
.
Solution: Use
generateMetadata()
for dynamic pages:
typescript
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
  const { id } = await params
  const post = await fetch(`/api/posts/${id}`).then(r => r.json())

  return {
    title: post.title,
    description: post.excerpt,
  }
}

原因: 字体变量未应用到HTML元素。
解决方法: 将字体变量应用到
<html>
<body>
:
typescript
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html className={inter.variable}> {/* ✅ 应用变量 */}
      <body>{children}</body>
    </html>
  )
}

15. Error:
next/font
font not loading

16. 错误: Environment variables not available in browser

Cause: Font variable not applied to HTML element.
Solution: Apply font variable to
<html>
or
<body>
:
typescript
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html className={inter.variable}> {/* ✅ Apply variable */}
      <body>{children}</body>
    </html>
  )
}

原因: 仅服务器端的环境变量无法暴露给浏览器。
解决方法: 对于客户端访问的变量,添加
NEXT_PUBLIC_
前缀:
bash
undefined

16. Error: Environment variables not available in browser

.env

Cause: Server-only env vars are not exposed to browser.
Solution: Prefix with
NEXT_PUBLIC_
for client-side access:
bash
undefined
SECRET_KEY=abc123 # 仅服务器端可用 NEXT_PUBLIC_API_URL=https://api # 浏览器端可用

```typescript
// Server Component(两者都可用)
const secret = process.env.SECRET_KEY
const apiUrl = process.env.NEXT_PUBLIC_API_URL

// Client Component(仅公开变量可用)
const apiUrl = process.env.NEXT_PUBLIC_API_URL

.env

17. 错误: Server Action not found

SECRET_KEY=abc123 # Server-only NEXT_PUBLIC_API_URL=https://api # Available in browser

```typescript
// Server Component (both work)
const secret = process.env.SECRET_KEY
const apiUrl = process.env.NEXT_PUBLIC_API_URL

// Client Component (only public vars work)
const apiUrl = process.env.NEXT_PUBLIC_API_URL

错误信息:
Error: Could not find Server Action
原因: 缺少
'use server'
指令。
解决方法: 添加
'use server'
:
typescript
// ❌ 旧写法
export async function createPost(formData: FormData) {
  await db.posts.create({ ... })
}

// ✅ 新写法
'use server'

export async function createPost(formData: FormData) {
  await db.posts.create({ ... })
}

17. Error: Server Action not found

18. 错误: TypeScript path alias not working

Error:
Error: Could not find Server Action
Cause: Missing
'use server'
directive.
Solution: Add
'use server'
:
typescript
// ❌ Before
export async function createPost(formData: FormData) {
  await db.posts.create({ ... })
}

// ✅ After
'use server'

export async function createPost(formData: FormData) {
  await db.posts.create({ ... })
}

原因:
tsconfig.json
中的
baseUrl
paths
配置错误。
解决方法: 正确配置:
json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./*"],
      "@/components/*": ["./app/components/*"]
    }
  }
}
参考文档:
references/top-errors.md

18. Error: TypeScript path alias not working

19. 错误: Client-side navigation throttled with multiple redirects

Cause: Incorrect
baseUrl
or
paths
in
tsconfig.json
.
Solution: Configure correctly:
json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./*"],
      "@/components/*": ["./app/components/*"]
    }
  }
}
See Reference:
references/top-errors.md

错误信息:
Throttling navigation to prevent the browser from hanging
来源: GitHub Issue #87245
原因: 当
proxy.ts
(或
middleware.ts
)执行重定向以添加查询参数,同时Server Component也调用
redirect()
添加不同的查询参数时,生产构建中通过
<Link>
进行的客户端导航会失败。这是Next.js 14到16的回归问题。
症状:
  • next dev
    (开发模式)中正常工作
  • 直接访问URL(完整页面加载)时正常工作
  • 生产构建中通过
    <Link>
    进行客户端导航时失败
  • 预取导致无限重定向循环
解决方法: 对导航到包含重定向逻辑的页面的链接禁用预取:
typescript
// ✅ 解决方法:禁用预取
<Link href="/my-route" prefetch={false}>
  Navigate
</Link>

19. Error: Client-side navigation throttled with multiple redirects

20. 错误: Cache Components fail with i18n dynamic segments

Error:
Throttling navigation to prevent the browser from hanging
Source: GitHub Issue #87245
Cause: When
proxy.ts
(or
middleware.ts
) performs a redirect to add query params AND a Server Component also calls
redirect()
to add different query params, client-side navigation via
<Link>
fails in production builds. This is a regression from Next.js 14 to 16.
Symptoms:
  • Works in
    next dev
    (development mode)
  • Works with direct URL access (full page load)
  • Fails with client-side navigation via
    <Link>
    in production build
  • Prefetch causes infinite redirect loop
Solution: Disable prefetch on links that navigate to pages with redirect logic:
typescript
// ✅ Workaround: Disable prefetch
<Link href="/my-route" prefetch={false}>
  Navigate
</Link>

错误信息: Route becomes dynamic despite
generateStaticParams
来源: GitHub Issue #86870
原因: 当使用
intlayer
next-intl
lingui
等国际化(i18n)框架时,缓存组件(
"use cache"
指令)在动态段上无法工作。即使在布局级别使用
generateStaticParams
,路由仍会变为动态。
原因分析: 每个i18n框架都需要访问
params
以获取区域设置。在Next.js 16中,访问
params
是一个异步调用,这会导致整个页面退出缓存。
解决方法: 在每个动态段级别添加
generateStaticParams
:
typescript
// app/[locale]/[id]/page.tsx
export async function generateStaticParams() {
  return [
    { locale: 'en', id: '1' },
    { locale: 'en', id: '2' },
    // ... 所有组合
  ];
}

'use cache'

export default async function Page({ params }: Props) {
  // 现在缓存生效
}
附加说明:
[locale]
动态段在编译期间会收到
_next
等无效值,这会导致初始化i18n提供程序时出现
RangeError: Incorrect locale information provided
错误。

20. Error: Cache Components fail with i18n dynamic segments

21. 错误: instanceof fails for custom error classes in Server Components

Error: Route becomes dynamic despite
generateStaticParams
Source: GitHub Issue #86870
Cause: Cache components (
"use cache"
directive) do NOT work on dynamic segments when using internationalization (i18n) frameworks like
intlayer
,
next-intl
, or
lingui
. Accessing
params
forces the route to be dynamic, even with
generateStaticParams
at the layout level.
Why It Happens: Every i18n framework requires accessing
params
to get the locale. Accessing
params
is an async call in Next.js 16, which opts the entire page out of caching.
Solution: Add
generateStaticParams
at EACH dynamic segment level:
typescript
// app/[locale]/[id]/page.tsx
export async function generateStaticParams() {
  return [
    { locale: 'en', id: '1' },
    { locale: 'en', id: '2' },
    // ... all combinations
  ];
}

'use cache'

export default async function Page({ params }: Props) {
  // Now caching works
}
Additional Context: The
[locale]
dynamic segment receives invalid values like
_next
during compilation, causing
RangeError: Incorrect locale information provided
when initializing i18n providers.

错误信息:
instanceof CustomError
returns
false
even though it is CustomError 来源: GitHub Issue #87614
原因: Server Components中的模块重复导致自定义错误类被加载两次,从而创建不同的原型。
解决方法: 使用
error.name
error.constructor.name
替代
instanceof
:
typescript
// ❌ 错误写法:instanceof不生效
try {
  throw new CustomError('Test error');
} catch (error) {
  if (error instanceof CustomError) { // ❌ 返回false
    // 永远不会执行到这里
  }
}

// ✅ 正确写法:使用error.name
try {
  throw new CustomError('Test error');
} catch (error) {
  if (error instanceof Error && error.name === 'CustomError') { // ✅ 返回true
    // 处理CustomError
  }
}

// ✅ 替代方案:使用constructor.name
if (error.constructor.name === 'CustomError') {
  // 处理CustomError
}

21. Error: instanceof fails for custom error classes in Server Components

22. 错误: TypeScript doesn't catch non-serializable props to Client Components

Error:
instanceof CustomError
returns
false
even though it is CustomError Source: GitHub Issue #87614
Cause: Module duplication in Server Components causes custom error classes to be loaded twice, creating different prototypes.
Solution: Use
error.name
or
error.constructor.name
instead of
instanceof
:
typescript
// ❌ Wrong: instanceof doesn't work
try {
  throw new CustomError('Test error');
} catch (error) {
  if (error instanceof CustomError) { // ❌ false
    // Never reached
  }
}

// ✅ Correct: Use error.name
try {
  throw new CustomError('Test error');
} catch (error) {
  if (error instanceof Error && error.name === 'CustomError') { // ✅ true
    // Handle CustomError
  }
}

// ✅ Alternative: Use constructor.name
if (error.constructor.name === 'CustomError') {
  // Handle CustomError
}

错误信息: Runtime error when passing functions/class instances to Client Components 来源: GitHub Issue #86748
原因: Next.js TypeScript插件无法检测到从Server Components传递给Client Components的非可序列化props。这会导致运行时错误,而这些错误在编译时无法被检测到。
原因分析: 只有可序列化数据(与JSON兼容)可以跨Server/Client边界传递。函数、类实例和Symbols无法被序列化。
解决方法: 仅传递可序列化props:
typescript
// ❌ 错误写法:函数不可序列化
const user = {
  name: 'John',
  getProfile: () => console.log('profile'), // ❌ 不可序列化
};
<ClientComponent user={user} />

// ✅ 正确写法:仅传递可序列化props
interface SerializableUser {
  name: string;
  email: string;
  // 无函数、无类实例、无Symbols
}

// ✅ 替代方案:在Client Component中创建函数
'use client';

export default function ClientComponent({ user }: { user: { name: string } }) {
  const getProfile = () => console.log('profile'); // 在客户端定义
  return <div onClick={getProfile}>{user.name}</div>;
}
运行时验证:
typescript
import { z } from 'zod';

const UserSchema = z.object({
  name: z.string(),
  email: z.string(),
});

type User = z.infer<typeof UserSchema>;

22. Error: TypeScript doesn't catch non-serializable props to Client Components

23. 错误: Turbopack production build fails with Prisma

Error: Runtime error when passing functions/class instances to Client Components Source: GitHub Issue #86748
Cause: The Next.js TypeScript plugin doesn't catch non-serializable props being passed from Server Components to Client Components. This causes runtime errors that are not detected at compile time.
Why It Happens: Only serializable data (JSON-compatible) can cross the Server/Client boundary. Functions, class instances, and Symbols cannot be serialized.
Solution: Only pass serializable props:
typescript
// ❌ Wrong: Function not serializable
const user = {
  name: 'John',
  getProfile: () => console.log('profile'), // ❌ Not serializable
};
<ClientComponent user={user} />

// ✅ Correct: Only serializable props
interface SerializableUser {
  name: string;
  email: string;
  // No functions, no class instances, no Symbols
}

// ✅ Alternative: Create functions in Client Component
'use client';

export default function ClientComponent({ user }: { user: { name: string } }) {
  const getProfile = () => console.log('profile'); // Define in client
  return <div onClick={getProfile}>{user.name}</div>;
}
Runtime Validation:
typescript
import { z } from 'zod';

const UserSchema = z.object({
  name: z.string(),
  email: z.string(),
});

type User = z.infer<typeof UserSchema>;

错误信息:
The 'path' argument must be of type string
来源: GitHub Discussion #77721
原因: Turbopack生产构建与Prisma ORM(v6.5.0+)不兼容。
解决方法: 使用webpack进行生产构建:
bash
npm run build -- --webpack
或在配置中禁用Turbopack:
typescript
// next.config.ts
const config: NextConfig = {
  experimental: {
    turbo: false,
  },
};

23. Error: Turbopack production build fails with Prisma

24. 错误: Turbopack exposes source code via source maps

Error:
The 'path' argument must be of type string
Source: GitHub Discussion #77721
Cause: Turbopack production builds fail with Prisma ORM (v6.5.0+).
Solution: Use webpack for production builds:
bash
npm run build -- --webpack
Or disable Turbopack in config:
typescript
// next.config.ts
const config: NextConfig = {
  experimental: {
    turbo: false,
  },
};

错误信息: Source code visible in production builds 来源: GitHub Discussion #77721
原因: Turbopack目前始终为浏览器构建生产环境Source Maps,这会暴露源代码。
解决方法: 禁用生产环境Source Maps:
typescript
// next.config.ts
const config: NextConfig = {
  productionBrowserSourceMaps: false,
};
或在部署中排除
.map
文件:
bash
undefined

24. Error: Turbopack exposes source code via source maps

.vercelignore

Error: Source code visible in production builds Source: GitHub Discussion #77721
Cause: Turbopack always builds production source maps for the browser, exposing source code.
Solution: Disable production source maps:
typescript
// next.config.ts
const config: NextConfig = {
  productionBrowserSourceMaps: false,
};
Or exclude
.map
files in deployment:
bash
undefined
*.map

---

.vercelignore

25. 错误: Module not found in production (Turbopack monorepo)

*.map

---
错误信息:
Module not found
in production despite successful local build 来源: GitHub Issue #87737
原因: 当
node_modules
结构不同时(pnpm、yarn workspaces、Monorepos),Turbopack生成的外部模块引用哈希会不匹配。
症状:
  • 本地构建成功,但CI/CD中构建失败
  • 打包后的引用哈希与实际模块文件不匹配
解决方法: 显式标记外部包:
typescript
// next.config.ts
const config: NextConfig = {
  experimental: {
    serverExternalPackages: ['package-name'],
  },
};

参考文档:
references/top-errors.md

25. Error: Module not found in production (Turbopack monorepo)

模板与资源

Error:
Module not found
in production despite successful local build Source: GitHub Issue #87737
Cause: Turbopack generates external module references with hashes that don't match when
node_modules
structure differs (pnpm, yarn workspaces, monorepos).
Symptoms:
  • Build succeeds locally but fails in CI/CD
  • Hash mismatches between bundled references and actual module files
Solution: Explicitly externalize packages:
typescript
// next.config.ts
const config: NextConfig = {
  experimental: {
    serverExternalPackages: ['package-name'],
  },
};

See Reference:
references/top-errors.md

Next.js 16专属模板(位于
templates/
目录下):
  • app-router-async-params.tsx
    - 异步参数迁移模式
  • parallel-routes-with-default.tsx
    - 必填的default.js文件示例
  • cache-component-use-cache.tsx
    - 使用
    "use cache"
    的缓存组件
  • revalidate-tag-cache-life.ts
    - 带cacheLife的更新版
    revalidateTag()
  • server-action-update-tag.ts
    - 用于读写一致性的
    updateTag()
  • proxy-migration.ts
    - 从middleware.ts迁移到proxy.ts
  • view-transitions-react-19.tsx
    - React 19.2 View Transitions
  • next.config.ts
    - Next.js 16配置示例
内置参考文档(位于
references/
目录下):
  • next-16-migration-guide.md
    - 完整的Next.js 15→16迁移指南
  • cache-components-guide.md
    - 缓存组件深度解析
  • proxy-vs-middleware.md
    - Proxy.ts vs middleware.ts对比
  • async-route-params.md
    - 异步路由参数破坏性变更详情
  • react-19-integration.md
    - Next.js 16中的React 19.2特性
  • top-errors.md
    - 18+常见错误及解决方案
外部文档:

Templates & Resources

版本兼容性

Next.js 16-Specific Templates (in
templates/
):
  • app-router-async-params.tsx
    - Async params migration patterns
  • parallel-routes-with-default.tsx
    - Required default.js files
  • cache-component-use-cache.tsx
    - Cache Components with
    "use cache"
  • revalidate-tag-cache-life.ts
    - Updated
    revalidateTag()
    with cacheLife
  • server-action-update-tag.ts
    -
    updateTag()
    for read-your-writes
  • proxy-migration.ts
    - Migrate from middleware.ts to proxy.ts
  • view-transitions-react-19.tsx
    - React 19.2 View Transitions
  • next.config.ts
    - Next.js 16 configuration
Bundled References (in
references/
):
  • next-16-migration-guide.md
    - Complete Next.js 15→16 migration guide
  • cache-components-guide.md
    - Cache Components deep dive
  • proxy-vs-middleware.md
    - Proxy.ts vs middleware.ts
  • async-route-params.md
    - Async params breaking change details
  • react-19-integration.md
    - React 19.2 features in Next.js 16
  • top-errors.md
    - 18+ common errors with solutions
External Documentation:

最低版本推荐版本
Next.js16.0.016.1.1+
React19.2.019.2.3+
Node.js20.9.020.9.0+
TypeScript5.1.05.7.0+
Turbopack(内置)稳定版
检查版本:
bash
./scripts/check-versions.sh

Version Compatibility

Token效率

PackageMinimum VersionRecommended
Next.js16.0.016.1.1+
React19.2.019.2.3+
Node.js20.9.020.9.0+
TypeScript5.1.05.7.0+
Turbopack(built-in)Stable
Check Versions:
bash
./scripts/check-versions.sh

预计Token节省: 65-70%
不使用本技能(从文档手动设置):
  • 阅读Next.js 16迁移指南: ~5k tokens
  • 阅读App Router文档: ~8k tokens
  • 阅读Server Actions文档: ~4k tokens
  • 阅读Metadata API文档: ~3k tokens
  • 试错修复: ~8k tokens
  • 总计: ~28k tokens
使用本技能:
  • 加载技能: ~8k tokens
  • 使用模板: ~2k tokens
  • 总计: ~10k tokens
  • 节省: ~18k tokens (~64%)
错误预防: 25种已记录的错误 = 100%错误预防

Token Efficiency

维护信息

Estimated Token Savings: 65-70%
Without Skill (manual setup from docs):
  • Read Next.js 16 migration guide: ~5k tokens
  • Read App Router docs: ~8k tokens
  • Read Server Actions docs: ~4k tokens
  • Read Metadata API docs: ~3k tokens
  • Trial-and-error fixes: ~8k tokens
  • Total: ~28k tokens
With Skill:
  • Load skill: ~8k tokens
  • Use templates: ~2k tokens
  • Total: ~10k tokens
  • Savings: ~18k tokens (~64%)
Errors Prevented: 25 documented errors = 100% error prevention

最后验证时间: 2026-01-21 技能版本: 3.1.0 变更: 添加了7种新错误(导航节流、国际化缓存、Turbopack局限性、instanceof失败、非可序列化props)。扩展了异步参数代码迁移工具的局限性、缓存默认值和并行路由边缘情况。
下一次审核: 2026-04-21(每季度) 维护者: Jezweb | jeremy@jezweb.net 仓库: https://github.com/jezweb/claude-skills
更新触发条件:
  • Next.js主版本/次版本发布
  • React主版本发布
  • API中的破坏性变更
  • Turbopack新特性
版本检查:
bash
cd skills/nextjs
./scripts/check-versions.sh

SKILL.md 结束

Maintenance

Last Verified: 2026-01-21 Skill Version: 3.1.0 Changes: Added 7 new errors (navigation throttling, i18n caching, Turbopack limitations, instanceof failures, non-serializable props). Expanded async params codemod limitations, caching defaults, and parallel routes edge cases.
Next Review: 2026-04-21 (Quarterly) Maintainer: Jezweb | jeremy@jezweb.net Repository: https://github.com/jezweb/claude-skills
Update Triggers:
  • Next.js major/minor releases
  • React major releases
  • Breaking changes in APIs
  • New Turbopack features
Version Check:
bash
cd skills/nextjs
./scripts/check-versions.sh

End of SKILL.md