# Prompt-to-PR：将 Next.js 14 迁移至 16

> 分步 SOP，用于将 Next.js 14 App Router 项目迁移至 Next.js 16 — 涵盖 Turbopack、React 19、异步 API 和缓存变更。

**Type:** Playbook  
**Tools:** Cursor, Claude Code, Codex, Windsurf  
**Stack:** Next.js, TypeScript  
**Difficulty:** hard  
**Updated:** 2026-06-08

---

Next.js 16 搭载了 React 19，异步 `params` 和 `searchParams`，重构了 fetch 缓存机制，并将 Turbopack 设为默认开发打包工具。本手册将代理的注意力集中在破坏性变更上，并防止其重写可正常运行的代码。

## 1. 需求

将 `next` 从 14.x 升级到 16.x（并将 `react`/`react-dom` 升级到 19.x），确保零功能回归。处理官方 codemod 报告的所有破坏性变更以及其未捕获的残留问题。

## 2. 初始提示

```txt title="First Prompt"
Migrate this project from Next.js 14 to Next.js 16. Follow these steps exactly.

Step 1 — run the official codemod:
  npx @next/codemod@latest upgrade latest --yes

Step 2 — manual fixes the codemod does not cover:
  a. `params` and `searchParams` in page.tsx/layout.tsx files are now Promises.
     Await them: `const { id } = await params;`
     Do NOT destructure params in the function signature.
  b. `cookies()`, `headers()`, `draftMode()` are now async.
     Add `await` before every call.
  c. `fetch()` is no longer cached by default in Route Handlers.
     Where caching is intentional, add `{ next: { revalidate: N } }`.
  d. Remove any `export const dynamic = 'force-dynamic'` that is now the default.
  e. Replace `<Image>` with the new `onLoad` prop signature if present.

Step 3 — update package.json:
  "next": "^16.0.0"
  "react": "^19.0.0"
  "react-dom": "^19.0.0"
  "@types/react": "^19.0.0"
  "@types/react-dom": "^19.0.0"

Do not change any business logic, UI, or database code.
List every file you changed and why.
```

## 3. 预期文件变更

```txt
package.json                          (next, react, react-dom, @types/react*)
src/app/**/page.tsx                   (await params / searchParams)
src/app/**/layout.tsx                 (await params where used)
src/app/api/**/route.ts               (await cookies/headers; fetch cache opts)
src/middleware.ts                     (if it uses deprecated config keys)
next.config.ts                        (remove deprecated options)
```

## 4. 审查清单

- `package.json` 将 `next` 锁定为 `^16.0.0`，React 锁定为 `^19.0.0`。
- 每个解构了 `params` 的页面/布局现在必须先 await 它。
- 服务器组件中不再存在同步的 `cookies()` 或 `headers()` 调用。
- 之前依赖隐式缓存的 `fetch` 调用现在具有明确的 `revalidate` 或 `no-store` 选项。
- `next.config.ts` 中没有已弃用的 `experimental.appDir` 或 `swcMinify` 键。
- TypeScript 通过 `bun tsc --noEmit` 编译，零错误。
- 代理没有重写任何 UI 组件或业务逻辑。

## 5. 测试命令

```bash
# Install updated deps
bun install

# Type-check
bun tsc --noEmit

# Dev build (Turbopack default in Next.js 16)
bun dev

# Production build — catches async-params errors at compile time
bun run build

# Run existing test suite
bun test
```

## 6. 常见失败

- **`params.id` 在 await 之前使用** — TypeScript 错误：`Property 'id' does not exist on type 'Promise<...>'`。代理有时在一个文件中 await 了 `params`，但在另一个文件中没有。
- **`cookies()` 未 await** — 运行时错误：`cookies() was called outside of a Server Component`。添加 `await`。
- **Turbopack 不兼容的 webpack 插件** — `next dev` 在自定义 `webpack()` 配置上出错。Turbopack 忽略 webpack 插件；迁移或进行条件性门控。
- **`@types/react` 版本冲突** — 旧组件库与 React 19 类型之间的 peer-dep 不匹配。将 `@types/react` 锁定到 19，必要时覆盖。
- **`useFormState` 已移除** — 被 `react` 的 `useActionState` 替代。codemod 通常能捕获到这一点，但请确认。

## 7. 修复提示

```txt title="Fix Prompt"
TypeScript reports: "Property 'slug' does not exist on type
'Promise<{ slug: string }>'" in src/app/blog/[slug]/page.tsx.

The page function signature must be:
  export default async function Page({ params }: { params: Promise<{ slug: string }> })

Then at the top of the function body:
  const { slug } = await params;

Apply the same fix to every other page or layout that destructures params
without awaiting. List every file changed.
```

## 8. PR 描述

```md title="PR description"
## Chore: Migrate Next.js 14 → 16 + React 19

**Breaking changes addressed**:
- `params` / `searchParams` are now `Promise`s — awaited in all pages/layouts
- `cookies()` / `headers()` are now async — awaited in all Server Components
- Fetch caching defaults changed — explicit `revalidate` added where needed
- Removed deprecated `next.config.ts` keys (`swcMinify`, `experimental.appDir`)

**Tooling**: dev server now uses Turbopack by default (`next dev --turbopack`).

Run `bun run build` to confirm zero type errors before merging.
```