EdgeCases Logo
Nov 2025
Next.js
Surface
8 min read

Next.js 16: Dynamic by Default, Turbopack Stable, proxy.ts

Explicit caching with 'use cache', Turbopack as default bundler, and proxy.ts for Node.js runtime clarity

nextjs
react
caching
turbopack
bundlers
middleware
performance
next16

Next.js 16 shifts from implicit to explicit caching, defaults to Turbopack for 2-10× faster builds, and introduces proxy.ts to clarify network boundaries. This release prioritizes predictability over magic—caching is opt-in, bundler choice is transparent, and AI-assisted debugging becomes standard in development.

Paradigm Shift: Dynamic by Default

Previous Next.js versions aggressively cached pages, components, and fetch responses automatically. Next.js 16 executes all dynamic code at request time by default. You must explicitly opt-in to caching with the new "use cache" directive:

// Next.js 15: Implicitly cached
export default function Page() {
  const data = await fetch('https://api.example.com/posts');
  return <PostList data={data} />;
}

// Next.js 16: Dynamic unless you cache
'use cache'; // Opt-in to caching
export default function Page() {
  const data = await fetch('https://api.example.com/posts');
  return <PostList data={data} />;
}

This aligns Next.js with traditional full-stack frameworks (Rails, Django, Laravel) where every request runs fresh unless you explicitly cache. Learn more about "use cache" and Cache Components.

Five Major Features

1. Cache Components with "use cache"

Fine-grained caching for pages, components, and functions. Supports cacheLife profiles for stale-while-revalidate patterns:

'use cache';
export const cacheLife = 'default'; // 15min stale, 1day revalidate

export async function UserProfile({ id }) {
  const user = await db.users.findById(id);
  return <Profile user={user} />;
}

Requires cacheComponents: true in next.config.ts. Completes Partial Pre-Rendering (PPR) by letting you cache specific parts while keeping others dynamic.

2. Turbopack: Now Stable Default

Turbopack replaces Webpack as the default bundler with measurable performance gains:

  • 2–5× faster production builds
  • Up to 10× faster Fast Refresh during development
  • File system caching (beta): Stores compiler artifacts between runs for even faster restarts
// Opt back to Webpack if needed
// package.json
{
  "scripts": {
    "dev": "next dev --webpack",
    "build": "next build --webpack"
  }
}

Deep dive into Turbopack performance characteristics.

3. proxy.ts Replaces middleware.ts

New file clarifies network boundaries by running on Node.js runtime (not Edge) with full filesystem and library access:

// proxy.ts
export default function proxy(request: NextRequest) {
  // Full Node.js APIs available
  const db = require('database-lib');

  if (!request.headers.get('authorization')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

middleware.ts remains available for Edge runtime scenarios but is deprecated. Learn when to use proxy.ts vs middleware.ts.

4. Next.js DevTools MCP

AI-assisted debugging via Model Context Protocol integration:

  • Unified browser + server logs in AI chat context
  • Automatic error stack trace analysis
  • Route-aware debugging (knows which page/API route you're on)
  • Compatible with Claude, ChatGPT, and other AI assistants

5. Enhanced Routing & Prefetching

Two critical performance improvements:

  • Layout deduplication: Shared layouts download once across multiple prefetches (50 product links = 1 layout download, not 50)
  • Incremental prefetching: Only retrieves uncached data, cancels requests when links leave viewport, reprioritizes on hover/click

See incremental prefetching edge cases and debugging strategies.

Breaking Changes That Matter

Async Request APIs

All request-context APIs now require await:

// Next.js 15: Synchronous access
export default function Page({ params, searchParams }) {
  const id = params.id;
  const query = searchParams.q;
}

// Next.js 16: Must await
export default async function Page({ params, searchParams }) {
  const { id } = await params;
  const { q } = await searchParams;
}

// Also applies to:
const cookieStore = await cookies();
const headersList = await headers();

Parallel Routes Require default.js

Explicit default.js files now mandatory in all parallel route slots to handle navigation fallbacks:

// app/@modal/default.js (required)
export default function Default() {
  return null; // Render nothing when modal isn't active
}

Removed Features

  • AMP support: Completely removed (use React Server Components instead)
  • next lint command: Use ESLint or Biome directly
  • serverRuntimeConfig/publicRuntimeConfig: Replaced by environment variables
  • Node.js 18 support: Requires Node.js 20.9+ and TypeScript 5.1+

New Caching APIs

revalidateTag() with cacheLife

import { revalidateTag } from 'next/cache';

// Now requires cacheLife profile for stale-while-revalidate
await revalidateTag('blog-posts', 'max'); // 'default' | 'max' | custom profile

updateTag() for Read-Your-Writes

New Server Action API that expires cache and immediately refreshes data in the same request:

import { updateTag } from 'next/cache';

export async function updateUserSettings(formData) {
  await db.users.update(formData);

  // Expire cache and fetch fresh data immediately
  await updateTag('user-settings');

  // User sees updated data without page refresh
}

refresh() for Partial Updates

Server Action that refreshes uncached data only (dynamic elements like notification counts):

import { refresh } from 'next/cache';

export async function checkNotifications() {
  await refresh(); // Refreshes dynamic parts, keeps cached parts
}

React 19.2 + Canary Features

Next.js 16 ships with React 19.2 and enables experimental canary features:

  • View Transitions: Animated element updates between navigations
  • useEffectEvent(): Extract non-reactive logic from Effects without re-running on every dependency change
  • Activity component: Background state management for long-running operations

Upgrade Strategy

# Automated codemod
npx @next/codemod@canary upgrade latest

# Manual upgrade
npm install next@latest react@latest react-dom@latest

Key Migration Steps

  1. Audit caching behavior: Previously cached pages now run dynamically. Add "use cache" to pages that should remain cached.
  2. Update async APIs: Wrap params, searchParams, cookies(), headers() with await.
  3. Test Turbopack: Should work transparently, but test critical build/deploy pipelines. Opt back to Webpack if needed.
  4. Migrate middleware.ts: Rename to proxy.ts or keep if using Edge runtime.
  5. Add default.js to parallel routes: Required for all parallel route slots.

Next.js 16 represents a philosophical shift: predictability over convenience. Dynamic by default, cache explicitly, and always know which runtime your code executes in.

Advertisement

Related Insights

Explore related edge cases and patterns

Next.js
Deep
Next.js 'use cache': Explicit Caching with Automatic Keys
9 min
Next.js
Deep
Turbopack: Next.js 16 Default Bundler (2-10× Faster)
8 min
Next.js
Deep
proxy.ts: Node.js Runtime for Next.js Request Interception
8 min
Next.js
Deep
Next.js 16 Incremental Prefetching: Smart Route Preloading
8 min

Advertisement