Next.js 15 → Next.js 16
Next.js 15 to 16 is a single, well-supported step. The work concentrates in the now fully-removed synchronous request APIs, Turbopack-by-default, the middleware-to-proxy rename, and the new revalidateTag(tag, profile) signature — much of it automated by the @next/codemod upgrade CLI.
Last verified · Updated May 22, 2026
Migrating from Next.js 15 to Next.js 16. This is a single major step focused on the fully-removed sync request APIs, Turbopack as the default bundler, the middleware-to-proxy rename, the new revalidateTag(tag, profile) signature, and a Node.js 20.9+ floor. The @next/codemod upgrade CLI automates most of the mechanical migration.
Should you upgrade directly?
Yes. Next.js 15 → 16 is a single, well-supported step with no intermediate stop. The biggest behavioral shift is that synchronous request-API access — which merely warned in 15 — is removed entirely in 16, so make sure that audit is complete before bumping.
Key differences
- Sync request APIs removed: await cookies/headers/draftMode/params/searchParams everywhere (the 15 warning shim is gone).
- Turbopack is the default bundler; next build fails on a detected webpack config unless you pass --webpack.
- middleware.ts → proxy.ts (Node.js runtime only); rename the function and skipMiddleware config flags too.
- revalidateTag(tag) errors in TS — pass a cacheLife profile, or use the new updateTag()/refresh() APIs.
- Minimums rise to Node.js 20.9+, TypeScript 5.1+, React 19.2.
- Removed: AMP, next lint, serverRuntimeConfig/publicRuntimeConfig, devIndicators options, experimental ppr/dynamicIO/useCache, unstable_rootParams; parallel-route slots need default.js.
- next/image config defaults changed (minimumCacheTTL, qualities, imageSizes, local-IP, redirects).
⚠ Finish the async request-API migration first
In 15 the synchronous shim let sync access slide with a dev warning. In 16 it is removed, so any straggler will break the build or runtime. Audit and await all of cookies/headers/draftMode/params/searchParams before you bump, and run
npx next typegenfor the type helpers.
Files and patterns to inspect
- Pages/layouts/routes still reading params or searchParams synchronously.
- Server code calling cookies(), headers(), or draftMode() without await.
- A middleware.ts/middleware.js file and skipMiddlewareUrlNormalize-style config flags.
- A custom webpack config in next.config (Turbopack is the default in 16).
- revalidateTag() calls and unstable_cacheLife/unstable_cacheTag imports.
- Parallel-route @slot directories missing default.js.
- AMP, next lint, serverRuntimeConfig/publicRuntimeConfig, experimental.ppr/dynamicIO usage.
- next/image config and local image src using query strings.
Next.js 15 vs Next.js 16
| Concern | Next.js 15 | Next.js 16 |
|---|---|---|
| Sync cookies()/headers()/params | Warns (temporarily allowed) | Removed — must await |
| Default bundler | webpack (Turbopack via --turbopack) | Turbopack (opt out --webpack) |
| Request interception | middleware.ts | proxy.ts (Node.js only) |
| revalidateTag() | revalidateTag(tag) | revalidateTag(tag, profile) |
| Minimum Node.js | 18.18+ | 20.9+ |
| React (App Router) | 19 | 19.2 |
AI-assisted migration workflow
Migrate this app from Next.js 15 to 16. Confirm Node 20.9+ and TypeScript 5.1+, audit for any remaining sync request-API access, a middleware file, a custom webpack config, and single-argument revalidateTag() calls. Run `npx @next/codemod@canary upgrade latest` and `npx next typegen`, await all request APIs, finish the middleware-to-proxy rename, move experimental.turbopack to top-level, add cacheLife profiles to revalidateTag() (or switch to updateTag), add default.js to parallel-route slots, and remove AMP/runtime-config/devIndicators/ppr usage. Run typecheck, lint, build, and tests after each step and report before continuing.Safety: Incremental edits only. Keep middleware (not proxy) if you require the edge runtime, and fall back to --webpack rather than reverting if a dependency blocks Turbopack.
Pre-migration checklist
- Green test suite and successful next build on Next.js 15
- All synchronous request-API access already awaited (it only warned in 15)
- Node.js bumped to 20.9+ and TypeScript to 5.1+
- Lockfile committed; dependencies audited for Turbopack and React 19.2 support
- A dedicated upgrade branch
Rollback strategy
- Keep the dependency bump, codemod, proxy rename, and manual fixes in separate commits.
- Fall back to
next build --webpackif a dependency blocks the Turbopack default rather than reverting the whole upgrade. - Revert to the prior next/react/react-dom versions and reinstall if a blocking incompatibility appears.
Related paths
- Next.js 13 to Next.js 15 Migration Guide — Earlier step in the upgrade chain
- Next.js 14 to Next.js 15 Migration Guide — Earlier step in the upgrade chain
- Migrate Next.js Pages Router to App Router — Adjacent migration
- Upgrade to Next.js 15 — Earlier step in the upgrade chain
- Upgrade to Next.js 16 — Upgrade path to same target
- Fix Next.js Build Errors — Related workflow
Official sources
- Next.js 16 — nextjs.org (reliability 98%)
- Upgrading: Version 16 — nextjs.org (reliability 98%)
Copy-ready AI prompts
Structured prompts for an AI coding assistant. Inspect first, then execute incrementally, and keep a human in the review loop.
Repo inspection: Repo inspection prompt (Next.js 16)
You are helping migrate a Next.js codebase from nextjs-15 to nextjs-16.
Do not edit files yet. First inspect the repository and report:
1. The exact next, react, and react-dom versions in package.json and the lockfile, plus the Node.js version (16 requires Node 20.9+) and TypeScript version (requires 5.1+).
2. Any remaining synchronous access to cookies(), headers(), draftMode(), params, or searchParams — these are FULLY removed in 16 and must be awaited. Also check params/id in opengraph-image/twitter-image/icon/apple-icon/sitemap generators.
3. A middleware.ts/middleware.js file and any config flags like skipMiddlewareUrlNormalize (these become proxy.ts and skipProxyUrlNormalize; note that proxy does not support the edge runtime).
4. A custom webpack config in next.config (Turbopack is the default in 16 and next build fails if a webpack config is detected) and any experimental.turbopack config to move to top-level.
5. revalidateTag() calls (now require a cacheLife profile second argument), and any unstable_cacheLife/unstable_cacheTag imports.
6. Usage of removed features: AMP (useAmp/next/amp/amp config), next lint, serverRuntimeConfig/publicRuntimeConfig, devIndicators.appIsrStatus/buildActivity, experimental.ppr/dynamicIO/useCache, unstable_rootParams, parallel-route slots missing default.js.
7. next/image config (minimumCacheTTL, qualities, imageSizes, domains) and any local image src using query strings.
8. The build, lint, and test commands.
Return: a migration risk summary, the files most likely to break, a suggested migration order, the commands to run before editing, and any questions that need human confirmation.Safety: Inspection only. The agent must not modify files in this step.
Works with Claude Code, Cursor, GitHub Copilot.
Migration execution: Migration execution prompt (Next.js 16)
Migrate this codebase from nextjs-15 to nextjs-16, one concern at a time.
Work in this order and pause for review after each: (1) confirm Node 20.9+ and TypeScript 5.1+, then bump next, react, and react-dom to latest, (2) run the official upgrade codemod (npx @next/codemod@canary upgrade latest), which updates the turbopack config, migrates next lint to the ESLint CLI, renames middleware to proxy, removes unstable_ prefixes, and drops experimental_ppr, (3) await every remaining synchronous request API (cookies/headers/draftMode/params/searchParams) — sync access is fully removed in 16; run `npx next typegen` for PageProps/LayoutProps/RouteContext helpers, (4) finish the middleware to proxy rename (function name + config flags) unless you must keep the edge runtime, (5) handle Turbopack as default — remove --turbopack from scripts, move experimental.turbopack to top-level turbopack, and only add --webpack if you must keep a webpack build, (6) add a cacheLife profile to revalidateTag() calls (or switch to updateTag in Server Actions), (7) fix removed config (AMP, runtime config, devIndicators, ppr/dynamicIO) and add default.js to parallel-route slots, (8) review next/image config defaults, (9) fix type errors.
After each step run the project's typecheck, lint, build, and tests, and report results before continuing. Do not refactor unrelated code.Safety: Apply changes incrementally and keep each step reviewable. Never bundle unrelated refactors.
Works with Claude Code, Cursor, GitHub Copilot.
Test plan
Commands
node -v # must be >= 20.9.0npx tsc --noEmitnpm run lintnpm test -- --watch=falsenpm run build
Manual checks
- Dynamic routes: load pages reading params/searchParams and confirm they await cleanly (sync access no longer compiles/runs).
- Auth/session: exercise routes using cookies() and headers() after awaiting them.
- Proxy: confirm renamed proxy.ts runs request interception identically to the old middleware (note: Node.js runtime only).
- Bundler: run next build with Turbopack (the default) and confirm output is correct; verify --webpack only if you intentionally kept webpack.
- Caching: confirm revalidateTag()/updateTag() invalidation behaves as intended after adding cacheLife profiles.
- Parallel routes: every @slot has a default.js and the build no longer fails.
- Images: spot-check next/image rendering given the new qualities/minimumCacheTTL/imageSizes defaults.
Regression risks
- Synchronous request-API access that compiled with a warning in 15 now hard-failing in 16.
- next build failing because a dependency injects a webpack config under Turbopack-by-default.
- Request interception silently broken if proxy.ts is created but the edge runtime was actually required.
- Stale caches because revalidateTag() lost its single-argument behavior and needs a cacheLife profile.
- Builds failing on parallel-route slots that lack default.js.
Acceptance criteria
- Typecheck, lint, tests, and next build (Turbopack) all pass on Node 20.9+.
- No synchronous request-API access remains anywhere in the app.
- middleware is renamed to proxy (or intentionally kept for edge), and revalidateTag() calls specify a cacheLife profile.
Frequently asked questions
Can I go straight to Next.js 16 from 15?
Yes. 15 → 16 is a single, well-supported major step with no intermediate stop. The main prerequisite is finishing the async request-API migration, since the synchronous shim that warned in 15 is fully removed in 16.
Will the codemod do everything?
It does most of the mechanical work — the version bump, moving the turbopack config, migrating next lint to the ESLint CLI, renaming middleware to proxy, removing unstable_ prefixes, and dropping experimental_ppr. You still manually await any remaining sync request APIs and add cacheLife profiles to revalidateTag() calls.