Developer Tools

Next.js 16 Deleted Implicit Caching. Now You Ask.

Next.js 16 swaps dynamicIO and useCache for cacheComponents, breaks single-argument revalidateTag, renames middleware to proxy and changes five image defaults.

TechLogHub Editorial
September 23, 2026
6 min read
0 views

Share Article

Cover comparing Next.js 15 implicit caching with Next.js 16 explicit use cache boundaries

Next.js 16 Deleted Implicit Caching. Now You Ask.

Quick answer: Next.js 16 removed the experimental.dynamicIO and experimental.useCache flags and replaced them with a top-level cacheComponents option. Turning it on is not a rename: uncached data outside a Suspense boundary becomes a build error. revalidateTag now demands a second argument, middleware is renamed to proxy, and five next/image defaults changed. Budget a sprint, not an afternoon.

Next.js spent years accumulating caching behaviour you did not ask for. Fetch results cached by default. A client router cache with opaque timings. Prefetching that pulled whole pages down on hover. Every one of those was defensible in isolation and collectively they made "why is this page stale" the single most common Next.js support question.

Version 16 is the correction. It is also part of a wider pattern: MCP deleted its session handshake for much the same reason, on the theory that implicit state is where the bugs live. The framework's own framing is "dynamic by default, with no hidden or implicit caching" — caching is now something you opt into, per boundary, with a directive. That is the right call. It is also a migration with sharp edges that the codemod will not smooth over for you.

cacheComponents is not a flag rename

If you were running experimental.dynamicIO or experimental.useCache, both flags are gone in 16. The replacement is:

const nextConfig: NextConfig = { cacheComponents: true }

Next.js is explicit that this is not a rename-only change: enabling it can surface build errors for uncached data outside of <Suspense>, and it requires adopting the Cache Components model wholesale. If you were not already using those experimental flags, the correct move during the 16 upgrade is to remove them and leave cacheComponents off until you have time to do the migration properly.

Partial Prerendering absorbed the same treatment. The experimental_ppr route segment config and the experimental PPR flag are removed; PPR is now something cacheComponents gives you. And PPR in 16 behaves differently from PPR in the 15 canaries — the official guidance for teams running it today is to stay on the 15 canary rather than upgrade and hope.

The cache API changes that break at compile time

revalidateTag('posts') is no longer valid. It now requires a cacheLife profile as a second argument — revalidateTag('posts', 'max') — and the single-argument form produces a TypeScript error. That is a good failure mode: you find out at build time, not when a customer sees stale data.

Two new APIs fill the gaps that revalidateTag was being abused to cover. updateTag is Server Actions only and gives read-your-writes semantics: it expires and immediately refreshes within the same request, so a user who submits a form sees their own change rather than stale content. refresh refreshes the client router from inside a Server Action.

The rule of thumb that falls out: revalidateTag for content where stale-while-revalidate is fine (blog posts, catalogues, docs), updateTag for anything a user just changed themselves. Most codebases have been using one API for both jobs and quietly shipping the wrong behaviour for the second.

Smaller but noisy: cacheLife and cacheTag are stable, so the unstable_ import aliases everyone copy-pasted can go.

The five image defaults that changed under you

These do not throw. They change behaviour silently, which makes them the most likely source of a post-upgrade bug report.

OptionWasNow
minimumCacheTTL60 seconds4 hours (14400s)
qualitiesall values allowed[75] only; others coerced
imageSizesincluded 1616 removed from defaults
maximumRedirectsunlimited3
local IP optimizationallowedblocked unless explicitly enabled

The qualities one deserves attention. If a component passes quality={90}, it is now coerced to 75 rather than erroring. Your images get smaller and slightly worse and nothing tells you. Same category of problem as the local IP restriction, which returns a 400 in split-horizon DNS setups with no obvious cause.

middleware became proxy, and the edge runtime did not come along

The middleware filename and named export are deprecated in favour of proxy, and config flags follow (skipMiddlewareUrlNormalize becomes skipProxyUrlNormalize). The codemod handles the mechanical part.

What it cannot handle: proxy runs on the Node.js runtime and that is not configurable. If your middleware relied on the edge runtime, you keep using middleware for now and wait for follow-up guidance. Deprecated-but-required is an uncomfortable place to be, and it is worth knowing before you start the upgrade rather than halfway through.

What 16.3 hands you for free

Next.js 16.3, released 3 August 2026, is mostly upside with no code changes. Turbopack uses up to 90% less memory in dev thanks to disk caching and memory eviction — Vercel reports its own dashboard dropping from 21.5 GB to 2 GB after compiling 50 routes. Disk caching now applies to next build too, with cached CI builds reported up to 5.5x faster on one project. The App Router rendering layer swapped web streams for native Node streams and handles up to 22% more requests under load.

You can also point next build at TypeScript 7 for type checking by bumping the local dependency — worth doing alongside the rest of a runtime and toolchain audit, not as an isolated change. If you are hand-writing types for API payloads while you are in there, the JSON to TypeScript converter does that part in the browser.

The opt-in half of 16.3 is Instant Navigations: cacheComponents: true plus partialPrefetching: true, giving per-link control over how much of a target page is prefetched, a devtools panel that surfaces slow navigations, and an instant() Playwright helper that fails a test when a navigation stops being instant. Next.js says these behaviours become the default in a future major. Treat that as the deadline for the Cache Components migration you are currently postponing.

The rest of the removals, briefly

AMP support is gone entirely. next lint is removed and builds no longer lint — use ESLint or Biome directly, both of which sit in the developer tools directory. serverRuntimeConfig and publicRuntimeConfig are gone in favour of environment variables. Parallel route slots now require explicit default.js files or the build fails. And synchronous access to cookies(), headers(), params and searchParams — the compatibility shim from 15 — is fully removed.

Turbopack is the default for both next dev and next build, and a project with a custom webpack config will fail the build rather than silently ignore it. If a plugin added that config on your behalf, the error will look like it came from nowhere. That is the same class of migration as Vite 8 swapping its bundler, and it rewards the same approach: change one thing at a time. Minimum runtime is Node 20.9 and minimum TypeScript is 5.1.


FAQ

Do I have to enable cacheComponents to upgrade to Next.js 16?

No. If you were not using experimental.dynamicIO or experimental.useCache, remove those flags and leave cacheComponents off. Enabling it is a separate migration that can surface build errors for uncached data outside Suspense.

What replaces revalidateTag with one argument?

Pass a cacheLife profile as the second argument, for example revalidateTag('posts', 'max'). If you need immediate expiry with read-your-writes behaviour instead of stale-while-revalidate, use updateTag inside a Server Action.

Can I keep using webpack in Next.js 16?

Yes, with the --webpack flag on next build. Without it, a project containing a custom webpack config fails the build rather than ignoring the config, which is deliberate — it prevents a silently misconfigured production build.

Is the edge runtime supported in proxy.ts?

No. proxy runs on the Node.js runtime and this is not configurable. Teams that need the edge runtime keep the deprecated middleware convention until further guidance ships in a minor release.

Why did my images get blurrier after upgrading?

The default images.qualities value is now [75]. A quality prop outside that array is coerced to the closest allowed value rather than throwing. Add the qualities you actually use to the config.

Should I adopt Instant Navigations now or wait?

It is opt-in today and Next.js states the behaviours will become default in a future major. Adopting early means doing the Cache Components work on your own schedule instead of during a forced upgrade, which is the stronger position.


Explicit caching is more work to write and far less work to debug. That trade has been worth making every time it has come up.

Stay Updated

Get the next deep dive in your inbox

Subscribe for product analysis, engineering explainers, and practical guides published on TechLogHub.

See what launched this week

One email a week: new and trending developer tools, fresh comparisons, and what shipped. Unsubscribe in one click.

Next.js 16 Caching: What cacheComponents Changes | Nextjs 16 Explicit Caching | Developer Tools | TechLogHub