Next 16: The Future of Next.js

Cache Components are a new set of features designed to make caching in Next.js both more explicit, and more flexible. They center around the new "use cache" directive, which can be used to cache pages, components, and functions, and which leverages the compiler to automatically generate cache keys wherever it’s used.
Unlike the implicit caching found in previous versions of the App Router, caching with Cache Components is entirely opt-in. All dynamic code in any page, layout, or API route is executed at request time by default, giving Next.js an out-of-the-box experience that’s better aligned with what developers expect from a full-stack application framework.
Cache Components also complete the story of Partial Prerendering (PPR), which was first introduced in 2023. Prior to PPR, Next.js had to choose whether to render each URL statically or dynamically; there was no middle ground. PPR eliminated this dichotomy, and let developers opt portions of their static pages into dynamic rendering (via Suspense) without sacrificing the fast initial load of fully static pages.
You can enable Cache Components in your next.config.ts file:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
// ...
}
export default nextConfig
Next.js 16 introduces Next.js DevTools MCP, a Model Context Protocol integration for AI-assisted debugging with contextual insight into your application.
The Next.js DevTools MCP provides AI agents with:
This enables AI agents to diagnose issues, explain behavior, and suggest fixes directly within your development workflow.
proxy.ts (formerly middleware.ts)proxy.ts replaces middleware.ts and makes the app’s network boundary explicit. proxy.ts runs on the Node.js runtime.
middleware.ts → proxy.ts and rename the exported function to proxy. Logic stays the same.import { NextRequest, NextResponse } from 'next/server'
export default function proxy(request: NextRequest) {
// ...
return NextResponse.redirect(new URL('/home', request.url))
}
In Next.js 16 the development request logs are extended showing where time is spent.

The build is also extended to show where time is spent. Each step in the build process is now shown with the time it took to complete.
▲ Next.js 16 (Turbopack)
✓ Compiled successfully in 615ms
✓ Finished TypeScript in 1114ms
✓ Collecting page data in 208ms
✓ Generating static pages in 239ms
✓ Finalizing page optimization in 5ms
The following features were previously announced in the beta release:
Turbopack has reached stability for both development and production builds, and is now the default bundler for all new Next.js projects. Since its beta release earlier this summer, adoption has scaled rapidly: more than 50% of development sessions and 20% of production builds on Next.js 15.3+ are already running on Turbopack.
With Turbopack, you can expect:
We're making Turbopack the default to bring these performance gains to every Next.js developer, no configuration required. For apps with custom webpack setups, you can continue using webpack by running:
next dev --webpack
next build --webpack
Turbopack now supports filesystem caching in development, storing compiler artifacts on disk between runs for significantly faster compile times across restarts, especially in large projects.
Enable filesystem caching in your configuration:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
turbopackFileSystemCacheForDev: true,
},
}
export default nextConfig
create-next-app has been redesigned with a simplified setup flow, updated project structure, and improved defaults. The new template includes the App Router by default, TypeScript-first configuration, Tailwind CSS, and ESLint.
Following the Build Adapters RFC, we've worked with the community and deployment platforms to deliver the first alpha version of the Build Adapters API.
Build Adapters allow you to create custom adapters that hook into the build process, enabling deployment platforms and custom build integrations to modify Next.js configuration or process build output.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
adapterPath: require.resolve('./my-adapter.js'),
},
}
export default nextConfig
Built-in support for the React Compiler is now stable in Next.js 16 following the React Compiler's 1.0 release. The React Compiler automatically memoizes components, reducing unnecessary re-renders with zero manual code changes.
The reactCompiler configuration option has been promoted from experimental to stable. It is not enabled by default as we continue gathering build performance data across different application types. Expect compile times in development and during builds to be higher when enabling this option as the React Compiler relies on Babel.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
reactCompiler: true,
}
export default nextConfig
Install the latest version of the React Compiler plugin:
npm install babel-plugin-react-compiler@latest
Next.js 16 includes a complete overhaul of the routing and navigation system, making page transitions leaner and faster.
Layout deduplication: When prefetching multiple URLs with a shared layout, the layout is downloaded once instead of separately for each Link. For example, a page with 50 product links now downloads the shared layout once instead of 50 times, dramatically reducing the network transfer size.
Incremental prefetching: Next.js only prefetches parts not already in cache, rather than entire pages. The prefetch cache now:
Trade-off: You may see more individual prefetch requests, but with much lower total transfer sizes. We believe this is the right trade-off for nearly all applications. If the increased request count causes issues, please let us know. We're working on additional optimizations to inline data chunks more efficiently.
These changes require no code modifications and are designed to improve performance across all apps.
Next.js 16 introduces refined caching APIs for more explicit control over cache behavior.
revalidateTag() (updated)revalidateTag() now requires a cacheLife profile as the second argument to enable stale-while-revalidate (SWR) behavior:
import { revalidateTag } from 'next/cache'
// ✅ Use built-in cacheLife profile (we recommend 'max' for most cases)
revalidateTag('blog-posts', 'max')
// Or use other built-in profiles
revalidateTag('news-feed', 'hours')
revalidateTag('analytics', 'days')
// Or use an inline object with a custom revalidation time
revalidateTag('products', { revalidate: 3600 })
// ⚠️ Deprecated - single argument form
revalidateTag('blog-posts')
The profile argument accepts built-in cacheLife profile names (like 'max', 'hours', 'days') or custom profiles defined in your next.config. You can also pass an inline { expire: number } object. We recommend using 'max' for most cases, as it enables background revalidation for long-lived content. When users request tagged content, they receive cached data immediately while Next.js revalidates in the background.
Use revalidateTag() when you want to invalidate only properly tagged cached entries with stale-while-revalidate behavior. This is ideal for static content that can tolerate eventual consistency.
updateTag() (new)updateTag() is a new Server Actions-only API that provides read-your-writes semantics, expiring and immediately reading fresh data within the same request:
'use server'
import { updateTag } from 'next/cache'
export async function updateUserProfile(userId: string, profile: Profile) {
await db.users.update(userId, profile)
// Expire cache and refresh immediately - user sees their changes right away
updateTag(`user-${userId}`)
}
This ensures interactive features reflect changes immediately. Perfect for forms, user settings, and any workflow where users expect to see their updates instantly.
refresh() (new)refresh() is a new Server Actions-only API for refreshing uncached data only. It doesn't touch the cache at all:
'use server'
import { refresh } from 'next/cache'
export async function markNotificationAsRead(notificationId: string) {
// Update the notification in the database
await db.notifications.markAsRead(notificationId)
// Refresh the notification count displayed in the header
// (which is fetched separately and not cached)
refresh()
}
This API is complementary to the client-side router.refresh(). Use it when you need to refresh uncached data displayed elsewhere on the page after performing an action. Your cached page shells and static content remain fast while dynamic data like notification counts, live metrics, or status indicators refresh.
The App Router in Next.js 16 uses the latest React Canary release, which includes the newly released React 19.2 features and other features being incrementally stabilized. Highlights include:
Learn more in the React 19.2 announcement.