Next.js 16 was released on October 21, 2025, with Turbopack as the default bundler, the new Cache Components model built on Partial Prerendering, and proxy.ts in place of middleware.ts (Next.js blog, 2025). We migrated three production projects from Next.js 15: a B2B SaaS dashboard, a content-heavy marketing site in two languages and an e-commerce storefront. This guide covers what actually got better, what broke and the migration plan we now use on every client project.
TL;DR: should you upgrade from Next.js 15 to 16?
Yes, for most App Router projects. The upgrade usually takes one to two working days, and the gains in build time and local development speed show up in the first week. The main exceptions are apps with a heavy custom webpack configuration, apps that still rely on synchronous params or cookies() access, and projects that ran the experimental PPR flag on a Next.js 15 canary.
| Area | Next.js 15 | Next.js 16 |
|---|---|---|
| Default bundler | webpack (Turbopack opt-in) | Turbopack, --webpack to opt out |
| Caching model | Implicit caching in the App Router | Opt-in via "use cache" and cacheComponents |
| Request interception | middleware.ts |
proxy.ts on the Node.js runtime |
params, cookies(), headers() |
Async with sync compatibility | Async only |
| Linting | next lint, runs in next build |
ESLint or Biome directly |
| Minimum Node.js | 18.18 | 20.9 |
What actually got better
Turbopack is the default, and builds are faster
Turbopack, the Rust-based bundler, is stable for both next dev and next build and is now the default for every project. Vercel reports 2–5× faster production builds and up to 10× faster Fast Refresh (Next.js 16 release notes). Our numbers sit inside that range:
- Cold production builds dropped by 35–60% across the three projects
- Hot reload in a project with 1,200+ files responds in 200–400 ms
- Filesystem caching keeps compiler artifacts on disk, so restarting
next devon a large repo no longer means a full recompile (Turbopack FileSystem Caching)
If a plugin injects a webpack config, next build now fails on purpose instead of silently ignoring it. Run next build --webpack as a stopgap while you migrate the config to turbopack options (upgrade guide).
Cache Components finally make PPR usable
Partial Prerendering (PPR) lets one route serve a static shell instantly and stream dynamic regions inside <Suspense> boundaries. In Next.js 16 it ships as part of Cache Components: you enable cacheComponents: true and mark what may be cached with the "use cache" directive. Everything else runs at request time by default (Cache Components docs).
// next.config.ts
import type { NextConfig } from "next"
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
// app/products/[id]/page.tsx
import { Suspense } from "react"
import { cacheLife, cacheTag } from "next/cache"
async function ProductInfo({ id }: { id: string }) {
"use cache"
cacheLife("hours")
cacheTag(`product-${id}`)
const product = await db.product.findUnique({ where: { id } })
return <ProductCard product={product} />
}
export default async function Page({ params }: PageProps<"/products/[id]">) {
const { id } = await params
return (
<>
<ProductInfo id={id} /> {/* cached, part of the static shell */}
<Suspense fallback={<StockSkeleton />}>
<LiveStock id={id} /> {/* dynamic, streamed per request */}
</Suspense>
</>
)
}
The big shift is mental. Caching is no longer something Next.js does to you; it is something you opt into per function or component. That removed most of the "why is this page stale?" tickets we used to get on Next.js 14 and 15.
Clearer cache invalidation APIs
revalidateTag() now takes a cacheLife profile as a second argument and serves stale content while it revalidates in the background. For forms where users must see their own change right away, the new updateTag() expires and re-reads data in the same request:
"use server"
import { revalidateTag, updateTag } from "next/cache"
export async function publishArticle(id: string) {
await db.article.publish(id)
revalidateTag("articles", "max") // readers get fresh data on the next visit
}
export async function updateProfile(userId: string, data: Profile) {
await db.user.update(userId, data)
updateTag(`user-${userId}`) // the author sees the change immediately
}
Server Actions remove most form boilerplate
Server Actions combined with React's useActionState replaced roughly 80% of our hand-written form code: no API route, no client-side fetch wrapper, no manual loading state. Next.js 16 also ships React 19.2 features such as View Transitions and useEffectEvent (React 19.2 announcement).
Smaller prefetch payloads
The router was rewritten with layout deduplication and incremental prefetching: a page with 50 product links downloads the shared layout once instead of 50 times (Next.js 16 release notes). Expect more prefetch requests in DevTools, but a much smaller total transfer.
What broke during our migrations
Most of the breakage is listed in the official upgrade guide. These are the items that actually cost us time.
- Synchronous request APIs are gone.
params,searchParams,cookies(),headers()anddraftMode()must be awaited. The mainupgradecodemod does not fix this; runnpx @next/codemod@canary next-async-request-api .separately and usenpx next typegento get typedPagePropshelpers. middleware.tsbecameproxy.ts. Rename the file and the exported function toproxy.proxyalways runs on Node.js, so if you depend on the Edge runtime, keepmiddleware.tsfor now; it is deprecated, not removed (proxy docs).next/imagedefaults changed.images.qualitiesnow defaults to[75], soquality={90}is silently coerced to 75 until you list 90 explicitly.minimumCacheTTLwent from 60 seconds to 4 hours, local images with query strings needimages.localPatterns, andimages.domainsis deprecated in favour ofremotePatterns. External SVGs still needdangerouslyAllowSVGtogether with a strictcontentSecurityPolicy(image config reference).next lintwas removed.next buildno longer lints, so add an explicit ESLint or Biome step to CI. The codemodnpx @next/codemod@canary next-lint-to-eslint-cli .migrates the script, and the Next.js ESLint plugin now defaults to flat config.- Parallel routes need
default.js. Every slot needs an explicitdefault.jsthat returnsnullor callsnotFound(), otherwise the build fails. - Runtime config is gone.
serverRuntimeConfigandpublicRuntimeConfigwere removed; readprocess.envon the server and useNEXT_PUBLIC_variables on the client. - i18n in Server Components. Libraries built around React context, such as
react-i18next, do not work inside Server Components. We moved to dictionaries loaded on the server and a smallLanguageProviderfor client components, the pattern described in the Next.js internationalization guide.
A custom Node.js server still works in Next.js 16 (custom server docs), but it opts you out of some optimizations. In two of the three projects we replaced ours with proxy.ts and route handlers and dropped the extra process.
What we do differently on new projects
Cache Components from day one. It forces you to split every page into a cached shell and dynamic islands. The code structure gets cleaner, and in our projects Lighthouse performance scores rose by 8–12 points, mostly from a faster Largest Contentful Paint (web.dev: LCP).
Mutations in Server Actions, API routes only for the outside world. We keep route handlers for webhooks, payment-gateway callbacks and public APIs, and move everything the UI triggers into Server Actions with updateTag().
The React Compiler on new code. Built-in support is stable since Next.js 16 and the compiler reached 1.0 (React Compiler v1.0). It removes most manual useMemo and useCallback, at the price of slower builds because it runs through Babel. We enable reactCompiler: true on greenfield projects and evaluate it separately on existing ones.
Measure real users, not build output. Next.js 16 removed the size and First Load JS columns from next build because they were inaccurate for Server Components. We track Core Web Vitals in production instead (Core Web Vitals).
Next.js 16 is the first release where we see no strong reason to stay on 15. Performance and DX outweigh the migration cost.
A 1–2 day migration plan
Upgrade Node.js. Next.js 16 requires Node.js 20.9 or later. We move straight to an active LTS line, Node.js 22 or 24 (Node.js release schedule), and TypeScript 5.1+.
Capture a baseline. Record build time, Lighthouse scores and Core Web Vitals for your top pages before touching anything.
Run the codemods.
npx @next/codemod@canary upgrade latest npx @next/codemod@canary next-async-request-api . npx next typegenFix the breaking changes:
proxy.ts, image config, parallel-routedefault.js, the lint step in CI and any webpack customizations.Decide on Cache Components separately. Turning on
cacheComponentsis not a rename; it surfaces build errors for uncached data outside<Suspense>. Follow the migration guide in a separate PR.Run E2E tests and Lighthouse and compare against the baseline. Our pipeline template is in CI/CD without pain: GitHub Actions for teams up to 20 developers.
Deploy to staging with real traffic for 24 hours, watching error rates, cache hit ratio and image optimization CPU.
Related guides
After the upgrade, measure what users feel with field data — see Core Web Vitals in 2026 — and review metadata, sitemaps and hreflang with Technical SEO for Next.js. If you are considering an installable app, compare the options in PWA vs native app.
Adding an AI assistant to a Next.js app? See building an AI chat in Next.js.
FAQ
Is Next.js 16 stable enough for production? Yes. Turbopack, the React Compiler integration and the new caching APIs are stable; only filesystem caching for builds and the Build Adapters API started as beta or alpha (Next.js 16 release notes).
Do I have to use Turbopack?
No. next dev --webpack and next build --webpack keep webpack, but new features land in Turbopack first.
Does the Pages Router still work?
Yes. Pages Router apps upgrade too, but Cache Components, Server Actions and proxy-first patterns are App Router features.
What happens to my middleware.ts?
It keeps working with a deprecation warning. Rename it to proxy.ts unless you need the Edge runtime.
Sources
- Vercel (2025). Next.js 16. Release announcement.
- Next.js docs. How to upgrade to version 16.
- Next.js docs. cacheComponents and Migrating to Cache Components.
- Next.js docs. Proxy.
- Next.js docs. updateTag, cacheLife.
- Next.js docs. Image component.
- React team (2025). React 19.2 and React Compiler v1.0.
- Node.js. Release schedule.
- web.dev. Core Web Vitals.