Next.js 16 upgrade checklist: every breaking change, the error you'll see, and the fix
By VibeBoot ·
Short on time? The SaaS Starter already has this wired up: $49 one-time, 14-day money-back guarantee.
Upgrading a Next.js 15 app to Next.js 16 is mostly renames and removals, but a few of them fail silently, and coding agents trained on older code keep writing the old version back in. This checklist covers every breaking change in the official upgrade guide, what you'll see when it bites, and the fix. Work through it top to bottom.
Everything below was checked on Nov 2, 2026 against the official Next.js 16 upgrade guide. Both VibeBoot starters already run on Next.js 16.4 with proxy.ts and async params.
The 60-second version
| Change | What you'll see | Fix |
|---|---|---|
Sync params, searchParams, cookies(), headers() removed | params.slug is undefined, or a type error | const { slug } = await params |
middleware.ts renamed to proxy.ts | "The middleware file convention is deprecated" warning | Rename the file and the function to proxy |
Turbopack is the default for next build | Build fails because a webpack config was found | Migrate it, or build with --webpack |
next lint removed | next lint script stops working | Run eslint directly |
revalidateTag(tag) needs a second argument | TypeScript error on revalidateTag | revalidateTag(tag, "max") or updateTag(tag) |
Parallel route slots need default.js | Build fails on an @slot folder | Add default.tsx that returns null |
next/image defaults tightened | Images 400, or come out at a different quality | Set images.qualities, localPatterns, remotePatterns |
serverRuntimeConfig and publicRuntimeConfig removed | getConfig() returns nothing useful | Use env vars |
| Node.js 18 dropped | Install or build fails on old CI images | Node.js 20.9 or newer |
Step 0: run the codemods first
npx @next/codemod@canary upgrade latestThat one command updates next.config for top-level turbopack, moves next lint to the ESLint CLI, renames middleware to proxy, drops the unstable_ prefix from stabilized APIs and removes experimental_ppr.
It does not migrate synchronous request APIs. If your app still reads params or cookies() without await, also run:
npx @next/codemod@canary next-async-request-api .Then search the diff for UnsafeUnwrapped and codemod error comments. Those are the spots it couldn't fix for you.
If you use Cursor or Claude Code, run npx @next/codemod@canary agents-md too. It points AGENTS.md at the docs bundled with your installed Next.js version, so the agent stops suggesting Next.js 14 patterns. More on that in Next.js 16 writes your AGENTS.md now.
1. Requirements: Node.js 20.9+, TypeScript 5.1+
Node.js 18 is no longer supported. Check your CI image, your Dockerfile and the Node version your host uses. Browser support is now Chrome/Edge/Firefox 111+ and Safari 16.4+. Upgrade @types/react and @types/react-dom along with next, react and react-dom.
2. Async request APIs: the silent one
Next.js 15 made params, searchParams, cookies(), headers() and draftMode() async, but kept a synchronous fallback. Next.js 16 removes the fallback.
The dangerous part is that params.slug on a Promise is just undefined. If your types still say { params: { slug: string } }, TypeScript stays quiet and the page renders "not found".
// Next.js 16
export default async function Page(props: PageProps<"/blog/[slug]">) {
const { slug } = await props.params;
const query = await props.searchParams;
return <h1>{slug}</h1>;
}Run npx next typegen to get the global PageProps, LayoutProps and RouteContext helpers. The same change applies in layout, route handlers, default, and the opengraph-image, twitter-image, icon and apple-icon files. In image files, id from generateImageMetadata is now a Promise too, and so is id in a sitemap that uses generateSitemaps.
Full fix with the agent prompt: params.slug is undefined (params is a Promise).
3. middleware.ts becomes proxy.ts
Rename the file and the exported function:
mv middleware.ts proxy.tsexport function proxy(request: Request) {}Two things to watch:
- Proxy runs on Node.js only. The
edgeruntime isn't supported inproxy, and you can't configure it. If you really need edge, the guide says to keep usingmiddlewarefor now. Don't blindly rename edge middleware. - Config flags were renamed too.
skipMiddlewareUrlNormalizeis nowskipProxyUrlNormalize.
With Auth.js v5 the whole file can be export { auth as proxy } from "@/auth". Walkthrough: Auth.js v5 on Next.js 16. Paste your old file into the free middleware.ts → proxy.ts converter to see the diff, or read the deprecation warning fix.
4. Turbopack is now the default
next dev and next build use Turbopack, so you can drop --turbopack from your scripts. If anything adds a webpack config (often a plugin you didn't write), next build fails on purpose. Your options:
- Migrate the config to Turbopack options.
experimental.turbopackis now top-levelturbopack. - Keep Webpack for production with
next build --webpack.
Two Turbopack differences that break builds: Sass imports with the ~ prefix (@import '~bootstrap/...') need the tilde removed, and client code that imports Node modules fails with Module not found: Can't resolve 'fs'. The right fix for that is to keep Node APIs in server code: Can't resolve 'fs' fix.
5. next lint is gone
next lint is removed, the eslint option in next.config is removed, and next build no longer lints. Replace the script with plain eslint (or Biome) and run it in CI yourself:
npx @next/codemod@canary next-lint-to-eslint-cli .@next/eslint-plugin-next now defaults to flat config, so move off .eslintrc if you haven't.
6. Caching APIs changed signature
revalidateTag("posts")is deprecated and a TypeScript error. UserevalidateTag("posts", "max")for stale-while-revalidate.- For "the user just saved and must see it", use the new
updateTag(tag)in a Server Action. refresh()fromnext/cacherefreshes the client router from a Server Action.cacheLifeandcacheTagare stable. Import them withoutunstable_.experimental.ppr,experimental.dynamicIOandexperimental.useCacheare removed. The replacement is top-levelcacheComponents: true, but that's a new model, not a rename. Don't switch it on just to finish the upgrade.
7. Parallel routes need default.js
Every @slot folder now needs an explicit default.js, or the build fails. To keep the old behavior:
export default function Default() {
return null;
}8. next/image defaults
None of these throw at build time. You find them when images look wrong or 400 in production.
images.qualitiesnow defaults to[75]. Aquality={90}prop gets coerced to 75, and direct/_next/imagerequests with otherqvalues return 400. List the qualities you use.- Local images with query strings (
/photo.png?v=1) needimages.localPatternswith asearchvalue. minimumCacheTTLwent from 60 seconds to 4 hours. Set it back if your images change often.16was dropped from the defaultimageSizes.- Images from local IPs are blocked unless you set
images.dangerouslyAllowLocalIP(private networks only). - At most 3 redirects are followed by default (
images.maximumRedirects). images.domainsandnext/legacy/imageare deprecated. UseremotePatternsandnext/image. Remote host errors: next/image hostname is not configured.
9. Removed config and APIs
serverRuntimeConfigandpublicRuntimeConfig: readprocess.envin server code, and useNEXT_PUBLIC_for values the browser needs. Callawait connection()first if a value must be read at request time instead of build time.- AMP:
next/amp,useAmpand theampconfig are gone. devIndicators:appIsrStatus,buildActivityandbuildActivityPositionare removed.unstable_rootParams: usenext/root-params.
10. Small behavior changes worth knowing
next devwrites to.next/dev, so dev and build can run at the same time. A lockfile stops twonext devprocesses on one project.process.argv.includes("dev")insidenext.configis nowfalseduringnext dev. CheckNODE_ENVinstead.- Next.js no longer overrides
scroll-behavior: smoothduring navigation. Adddata-scroll-behavior="smooth"to<html>if you want the old behavior. next buildno longer printssizeandFirst Load JS. Use Lighthouse or real Core Web Vitals instead.- React Compiler support is stable (
reactCompiler: true) but off by default, and it slows builds. Leave it off until the upgrade is done.
Done? Check these before you ship
npm run buildpasses on the Node version your host actually uses.- Every dynamic route renders with real data. Click into a
[slug]page, because the asyncparamsbug doesn't fail the build. - Auth still protects what it should after the
proxy.tsrename. Open a protected page in a private window. - Images load in production at the quality you expect.
- CI runs
eslintitself, sincenext buildno longer does. - Your
AGENTS.mdor Cursor rules say Next.js 16, so the agent doesn't undo all of this next week.
Still stuck on an error? Paste it into VibeBoot Doctor, which is free and runs in your browser, or browse the 70 fix pages for Next.js, Auth.js, Prisma and Stripe errors.