What Next.js 16 changes
Next.js · TypeScript · React
Next.js 16 makes Turbopack the default bundler and removes the last synchronous access to request APIs. It also renames middleware to proxy, and it ships with React 19.2. The App Router is the same router.
Turbopack is the default
next dev and next build use Turbopack without a --turbopack flag. Turbopack config moves from experimental.turbopack to a top-level turbopack key in next.config.ts. Compiler artifacts are cached on disk between runs.
A custom webpack config makes next build fail, so a leftover webpack plugin cannot silently be ignored. Opt out with next build --webpack, or move that config to Turbopack. The old Sass ~ import prefix is not supported. Write @import 'bootstrap/dist/css/bootstrap.min.css'.
next dev writes to .next/dev, so a dev server and a production build can run at the same time. A lock stops two dev servers, or two builds, from using one project.
Request APIs are async only
Next.js 15 made cookies(), headers(), draftMode(), params, and searchParams async, and still allowed synchronous reads for one version. Next.js 16 removes that compatibility.
const session = (await cookies()).get("session")?.value
const { slug } = await params
const { tab } = await searchParams
opengraph-image, twitter-image, icon, and apple-icon receive params and id as promises. sitemap receives id as a promise too. generateImageMetadata and generateSitemaps still see synchronous values.
npx next typegen generates PageProps, LayoutProps, and RouteContext so those promises are typed per route.
A route handler's second argument is { params: Promise<...> }. NextResponse is what the handler returns, not an argument it receives.
Cache APIs
revalidateTag takes a cacheLife profile as the second argument. revalidateTag('posts') is deprecated. revalidateTag('posts', 'max') marks the tag stale and serves the old value while the new one loads. revalidateTag('posts', 'page') expires the tag for the next visit.
updateTag is for Server Actions that must show the write in the same request. It expires the tag and refreshes immediately. refresh reloads the client router from a Server Action.
cacheLife and cacheTag are stable. Drop the unstable_ prefix.
Partial Prerendering is no longer experimental.ppr or experimental_ppr. Opt in with cacheComponents: true. That flag is not a rename. Uncached data outside <Suspense> fails the build. An app already on a Next.js 15 PPR canary should stay there until it migrates.
Proxy replaces middleware
Rename middleware.ts to proxy.ts, and rename the exported function to proxy. The proxy runtime is Node.js. It cannot run on the Edge runtime. Keep middleware if the code still needs Edge.
The proxy reads the incoming request. It rewrites, redirects, and sets headers. It is not a place for slow fetches or a full session check. Read a session cookie from req.cookies when a route should redirect signed-out visitors.
skipMiddlewareUrlNormalize is now skipProxyUrlNormalize.
React 19.2 and the compiler
The App Router uses React 19.2. The features that matter in product code are View Transitions, useEffectEvent, and Activity. View Transitions animate updates inside a transition or a navigation. useEffectEvent holds logic that an effect should call without re-running when that logic changes. Activity hides a subtree with display: none, keeps its state, and cleans up its effects.
React Compiler support is stable and off by default. Set reactCompiler: true and install babel-plugin-react-compiler. Compile times go up because the compiler uses Babel. Turn it on to measure a real app, not as part of the upgrade itself.
Prefetching also changed, with no code change. A shared layout is downloaded once when several links use it. Next.js prefetches only the segments that are not already cached. The network shows more small requests and less total data.
Images, lint, and removals
images.minimumCacheTTL defaults to 4 hours, up from 60 seconds. The default imageSizes list no longer includes 16. images.qualities defaults to [75], and any other quality prop is coerced to the nearest allowed value. Local image URLs with a query string need images.localPatterns.search. Optimization of local IP addresses is blocked unless images.dangerouslyAllowLocalIP is set. Image redirects default to a maximum of 3. next/legacy/image and images.domains are deprecated. Use next/image and images.remotePatterns.
next lint is gone. next build does not lint. ESLint runs as eslint ., and @next/eslint-plugin-next expects flat config.
Also removed: AMP, serverRuntimeConfig, publicRuntimeConfig, and the devIndicators options appIsrStatus, buildActivity, and buildActivityPosition. Read environment variables directly. Prefix public ones with NEXT_PUBLIC_.
Node.js 20.9 is the minimum. Node 18 is unsupported. TypeScript 5.1 is the minimum. Browsers are Chrome 111, Edge 111, Firefox 111, and Safari 16.4.
Parallel route slots now require a default.js. The build fails without one. Return null, or call notFound(), to keep the old behavior.