· nextjs / react / turbopack

Next.js 16 — an opinionated take after a real ship

Turbopack GA is the headline. The caching model flip is what will bite you. Upgrade if dev speed is your bottleneck; hold if you rely on webpack config or Edge Middleware auth.

By Ethan

2,058 words · 11 min read

Upgrade if you need faster dev loops and can spend half a day auditing caching behavior. Hold if your app has custom webpack loaders, Edge Middleware for auth or geolocation, or a large monorepo where build RAM is already a concern. Next.js 16 is a genuine step forward — Turbopack GA and React 19.2 ship real speed — but the silent caching model flip will catch you off guard if you don’t look for it.

Who this is for

You’re running Next.js 14 or 15 and deciding whether to upgrade now or wait for the ecosystem to settle. If you’re starting a greenfield project in 2026, start on 16 — the default-dynamic caching model is cleaner once you understand it.

If you’re happy, stable in production, and have no performance complaints, there’s no fire here. This is a worthwhile upgrade, not an emergency one.

What changed

Four things matter. The rest is maintenance.

Turbopack GA

Turbopack exits beta and becomes the recommended dev bundler. next dev --turbo is gone; Turbopack is now the default. If you want webpack back, pass --webpack explicitly.

The dev speed numbers are real. Vercel measured on actual codebases, not contrived payloads:

  • Dev restart with file-system cache: react.dev 10×, nextjs.org 5×, one large internal app 14×
  • Server refresh (16.2): 59ms → 12.4ms — a 79% drop
  • next dev startup (16.2): ~87% faster vs 16.1

The startup improvement alone changes the experience. “Wait for it” becomes “it’s ready.”

The catch: Turbopack’s output optimization is not yet equivalent to webpack’s. Some projects see significantly larger bundles. More on that in the gotchas section.

Cache Components (experimental)

"use cache" is a new directive that marks pages, components, or functions for caching across requests, independent of route-level caching. It requires cacheComponents: true in next.config.ts. It’s experimental and not in the stable API yet. Don’t build production features on it, but know it’s coming — it fills the gap between full static routes and per-request rendering.

proxy.ts

A new file at app/proxy.ts runs on the Node.js runtime and handles rewrites and redirects before the router. Think of it as a Node-side routing layer.

proxy.ts is the official replacement for middleware.tsmiddleware.ts is deprecated and will be removed in a future version. The critical caveat: proxy.ts is Node.js only. Edge-runtime capabilities (geo headers, V8 isolates) are not yet supported in proxy.ts. Teams relying on edge-specific features must keep middleware.ts for now and plan to migrate once Next.js ships edge support for proxy.ts.

React 19.2

Next.js 16 ships React 19.2. The relevant part: RSC rendering is 25–60% faster depending on payload complexity, fixed upstream in facebook/react#35776. You don’t write any new code to get this. It’s a free speedup on page rendering.

Upgrade experience

Start with the official codemod:

npx @next/codemod@latest upgrade

The codemod handles the mechanical work:

  • Converts synchronous calls to cookies(), headers(), params, and searchParams to async (await cookies(), etc.)
  • Updates renamed imports
  • Removes the webpack key from next.config.js — the build fails hard if webpack config is present in 16

After the codemod, you still have manual work.

Routes that relied on implicit static caching. Next.js 14 defaulted to static rendering. 16 flips the default to dynamic. Any route that was getting cached implicitly — because it never called cookies() and you never set dynamic = 'force-dynamic' — may now render dynamically on every request. No error appears. No warning fires. Your DB gets hit more often and your Lighthouse score drops. The codemod has no way to detect this. You have to compare next build output between versions yourself.

Parallel routes without default.js. Every @slot directory needs a default.js. In 15, a missing default.js could silently produce a 404. In 16, it’s a hard build error. Run next build locally after the codemod and clear every slot error before pushing.

Edge Middleware. The codemod doesn’t add, modify, or warn about middleware.ts. middleware.ts is deprecated — proxy.ts is its official replacement — but proxy.ts doesn’t yet support the Edge runtime. If you have geolocation or auth logic running on the Edge runtime, keep middleware.ts for now and plan to migrate once edge support lands in proxy.ts.

Custom webpack loaders. Remove the webpack key entirely, or production builds break. If you used webpack loaders for CSS modules, SVG imports, or custom asset transforms, find the Turbopack equivalents before upgrading.

Realistic timeline: 2–4 hours for a 30-route app with no webpack customization. Budget a full day for a large app with Edge Middleware, parallel routes, and custom webpack loaders.

Gotchas

The caching flip is a silent regression

This is the one that ships bugs. The full-dynamic default is actually a better mental model — you can reason about it more easily than “static unless proven otherwise.” But the migration path has no guardrails.

The specific risk: a page that returned a stale static response in 15 now makes a fresh DB query on every request in 16. If your DB handles the extra load, users notice nothing. If it doesn’t, you see latency spikes with no obvious cause.

Before you merge the upgrade PR, diff your next build output. Every page that switched from ○ Static to ƒ Dynamic needs a decision: is dynamic rendering what you want here, or do you need to explicitly opt back into caching with export const dynamic = 'force-static' or unstable_cache?

Turbopack bundle size regressions

Turbopack compiles faster. It doesn’t always produce smaller output. Two community-reported cases:

  • One project saw First-load JS jump from 0.57 MB to 2.6 MB — a 4.6× increase — after switching to Turbopack
  • Cal.com reported a +72% First-load JS median after enabling Turbopack builds

Root cause: Turbopack’s Inner Graph Optimization is not yet equivalent to webpack’s tree-shaking and chunk deduplication. Shared library code gets duplicated in some configurations.

This doesn’t affect every project. The Turbopack team is actively closing the gap. But if First-load JS is a key metric for you, run the bundle analyzer before and after. Switching back to --webpack for production builds is a supported escape hatch.

Large monorepos hit hard limits

On projects with 15+ packages and thousands of modules, Turbopack’s current behavior:

  • Initial cold compile: 15–25 seconds (slower than webpack on the first run)
  • Peak RAM: 10+ GB in community reports
  • File-system cache (.next/cache): can grow to 15 GB without pruning

The warm cache after that first compile is fast. But the cold start and RAM ceiling are real constraints on large CI machines. If you run ephemeral CI containers with fixed memory limits, test Turbopack builds in CI before committing to the upgrade.

Add a cache-busting step to your CI pipeline to prevent disk bloat:

# before each build
rm -rf .next/cache

Or limit the cache size in next.config.js once that configuration option ships in a point release.

proxy.ts replaces middleware.ts — but not fully yet

proxy.ts is the official replacement for middleware.ts. middleware.ts is deprecated and will be removed in a future version. But the migration isn’t a drop-in.

proxy.ts runs on Node.js. middleware.ts runs on the Edge runtime (V8-based, V8 isolates, no Node APIs). Edge-runtime capabilities are not yet supported in proxy.ts.

Vercel’s edge network injects geo headers (x-vercel-ip-country, x-vercel-ip-city) at the Edge runtime level. Reading those headers in middleware.ts works because the code runs at the same layer. Reading them in proxy.ts doesn’t work the same way — yet.

If your middleware.ts does any of: JWT verification, cookie inspection, IP-based routing, Cloudflare KV lookups — keep it in middleware.ts until the Next.js team ships edge support for proxy.ts. Plan the migration, but don’t rush it.

Async params — the shim is gone

Next.js 15 shipped a backward-compatibility shim that allowed synchronous access to params and searchParams on dynamic route segments. In 16, the shim is removed. If the codemod missed any of your sync accesses (they’re possible in edge cases with custom wrappers), you’ll see runtime errors on dynamic routes.

After the codemod, do a full search for any remaining sync access patterns:

grep -rn "params\." app/ --include="*.tsx" --include="*.ts" | grep -v "await"

Review each hit — not every match is a bug, but it’s worth the scan.

Benchmarks

All numbers are version-pinned and sourced. Methodology is from the original publisher, not re-verified here.

Dev speed — Turbopack (sources: nextjs.org/blog/next-16-1 for FS cache restarts; nextjs.org/blog/next-16-2-turbopack for server refresh; nextjs.org/blog/next-16-2 for startup)

Metric16.116.2Change
next dev startupbaseline~87% faster
Server refresh59ms12.4ms−79%
Dev restart — react.devbaseline10× faster (FS cache)
Dev restart — nextjs.orgbaseline5× faster (FS cache)
Dev restart — large internal appbaseline14× faster (FS cache)

RSC rendering — React 19.2 (source: nextjs.org/blog/next-16-2)

Payload complexitySpeedup
Simple components26%
Complex nested trees60%

Build time — Turbopack vs webpack (community reports, source: nextjs.org/docs upgrade discussions)

ProjectwebpackTurbopackDelta
Community report A~10 min~10 sec−98%
Community report B797s314s−61%

Build time reports are community-sourced, not Vercel-measured. Treat them as directional.

Bundle size risk (source: github.com/vercel/next.js/discussions/86967, 85744)

ProjectBeforeAfterChange
Anonymous project0.57 MB2.6 MB+4.6×
Cal.com (First-load JS median)baseline+72%

Recommendation matrix

Your situationRecommendation
Greenfield project in 2026Start on 16 — the dynamic-default model is cleaner
Running 14/15, no custom webpack, no Edge Middleware authUpgrade — codemod + 2–4h caching audit
Custom webpack loadersFind Turbopack equivalents first; then upgrade
Edge Middleware for auth or geolocationUpgrade with validation — middleware.ts still works, but test it
Large monorepo (15+ packages, CI with memory limits)Benchmark Turbopack RAM and cache size before committing
Bundle size is a primary metricRun bundle analyzer before flipping — check First-load JS delta
On Vercel Pro or TeamYes — FS cache and per-route cache-hit dashboards make the Turbopack gains concrete
Deploying to Railway, Render, or CoolifyBuild Adapters are stable in 16.2 — see the toolchew hosting comparison for deployment-specific notes

Verdict

Upgrade if dev iteration speed matters and you have the half-day to audit the caching flip. The Turbopack dev experience is noticeably faster once the FS cache is warm, and you get React 19.2 RSC rendering improvements at no cost.

Hold if you have custom webpack loaders you haven’t replaced, bundle size is load-bearing and you haven’t benchmarked Turbopack output, or your monorepo already strains CI memory limits.

The caching model flip is the most dangerous part of this upgrade — not because it’s a bad design, but because it’s invisible. Every team that skips the pre/post next build comparison is shipping a latency regression they’ll diagnose weeks later.

Caveats

The bundle size regressions cited here come from community discussion threads, not a controlled benchmark run by this publication. If your production app’s performance depends on bundle size, measure it yourself — don’t rely on Cal.com’s experience as representative of your workload.

The caching model change has no automated diff tool. Manual review of next build output is currently the only reliable way to catch routes that flipped from static to dynamic.

Toolchew has an affiliate relationship with Vercel. The Vercel Pro recommendation in the matrix above is based on the FS cache and observability features being genuinely useful for Turbopack workloads — not on the affiliate relationship. If those features didn’t add value, the row wouldn’t be there.

References

  1. Next.js 16 release
  2. Next.js 16.1 release
  3. Next.js 16.2 release
  4. Turbopack benchmark methodology
  5. Version 16 upgrade guide
  6. Community: caching regressions discussion
  7. Community: bundle size regressions
  8. Community: monorepo issues
  9. React 19.2 blog post
  10. React RSC rendering fix — facebook/react#35776