Back

What Changed in Next.js 16

By Pablo Fernandez2 min read

Next.js 16 landed in late 2025, and most of what changed is about caching and compilation moving from things you configure to things that happen by default. This is a tour of the parts I actually reached for when I moved this site onto it.

The "use cache" directive

The old story for caching a Server Component was a mix of revalidateTag, unstable_cache, and a lot of guessing about what was static. Next.js 16 turns it into a directive you put at the top of a function. Everything the function returns becomes a cache boundary you can reason about.

components/stats.tsx
"use cache"
 
async function UserStats({ userId }: { userId: string }) {
  const stats = await db.stats.findUnique({ where: { userId } });
 
  return (
    <div className="grid grid-cols-3 gap-4">
      <div>Posts: {stats.posts}</div>
      <div>Likes: {stats.likes}</div>
      {/* This subtree is cached on its own */}
    </div>
  );
}

The win is that the caching lives next to the code it caches, so you can see which parts of the tree are static and which are dynamic without cross-referencing a config file.

Turbopack is the default bundler

Turbopack is now the default for both development and production, not an opt-in flag. In day-to-day work the thing you feel is Fast Refresh: edits show up fast enough that you stop noticing the compile step, which is the whole point of a dev loop. The build logs also got more useful, with a breakdown of where compile time goes.

The React Compiler, on by default

New projects ship with the React Compiler enabled. It handles the memoization you used to write by hand, so useMemo and useCallback mostly leave your code. You describe the render and the compiler works out what can be skipped. The practical effect is fewer dependency arrays to get wrong.

components/search.tsx
// No manual useMemo: the compiler decides what to skip.
export function SearchResults({ items, filter }) {
  const filtered = items.filter(item => item.name.includes(filter));
 
  return (
    <ul>
      {filtered.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

Starting a project

Creating an app is one command, and Turbopack plus the React Compiler are the defaults when you accept the prompts:

Bash
bun create next-app@latest

proxy.ts in place of middleware

Next.js 16 renames the middleware.ts file to proxy.ts. The export is a plain function that runs before a request is routed, which is where you do redirects, auth checks, and rewrites. Here is the same "send unauthenticated users to the login page" guard I use on this site:

proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function proxy(request: NextRequest) {
  const { pathname } = request.nextUrl
 
  if (pathname.startsWith('/dashboard') && !request.cookies.get('access_token')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
 
  return NextResponse.next()
}
 
export const config = {
  matcher: ['/dashboard/:path*'],
}

The matcher decides which paths the function even runs on, so an ordinary page request pays nothing.

View Transitions

Next.js 16 sits on React 19.2 and exposes the browser's View Transitions API, so page-to-page navigations can animate instead of cutting. You opt a transition in where you want it rather than wiring an animation library through the router.

Where it leaves you

Most of the release pulls in the same direction: the caching and memoization you used to hand-tune are now defaults you occasionally override, not machinery you assemble. If you are upgrading, the two changes to plan for are the use cache model and the middleware.ts to proxy.ts rename. The rest you mostly get for free by taking the new defaults.

Related articles

Four Layers of What an Agent Knows

One AI agent, many companies, many people. Its knowledge comes in stacked layers, and the work is keeping each layer from leaking past who it belongs to.

k3s vs k0s vs Kubernetes

All three pass the same conformance tests. What separates them is everything between an API server and an HTTPS URL, and that's why pfrctl runs k3s.

An Anti-Scraping Seal

A price on a page is a fact someone paid to produce, public for anything to copy. I'm building an anti-bot seal to change what that copy costs.