Team4 min read

Next.js 16 vs 15: Complete Upgrade Guide

Next.js 16 represents one of the most significant updates to the framework, with Turbopack becoming the default bundler and introducing revolutionary caching strategies. This guide covers everything you need to know about upgrading from Next.js 15 to 16.

Quick Start

Upgrade to Next.js 16 using the automated migration tool:

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

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

# For TypeScript users
npm install @types/react@latest @types/react-dom@latest

Performance Highlights

  • 2-5× faster production builds with Turbopack
  • Up to 10× faster Fast Refresh in development
  • 50% reduction in network transfer for shared layouts
  • Instant navigation with Cache Components and PPR

Major Changes Overview

FeatureNext.js 15Next.js 16
Default BundlerWebpack (Turbopack optional)Turbopack (no config needed)
Build SpeedStandard2-5× faster
Fast RefreshStandardUp to 10× faster
CachingImplicit, time-basedExplicit, event-based with "use cache"
Middleware Filemiddleware.tsproxy.ts
Request APIsSynchronousAsync/Promise-based
React CompilerExperimentalStable
Node.js Minimum18.x20.9+
React Version19.x19.2+

Turbopack is Now Default

This is the most impactful change. In Next.js 15, Turbopack was optional and required explicit configuration. Now it's your default bundler with no flags needed.

Before (Next.js 15)

{
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build --turbopack"
  }
}

After (Next.js 16)

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  }
}

Turbopack Configuration

The experimental.turbopack configuration is now a top-level option:

import type { NextConfig } from 'next'

// Next.js 15 - experimental.turbopack
const nextConfig: NextConfig = {
  experimental: {
    turbopack: {
      // options
    },
  },
}

// Next.js 16 - turbopack at top level
const nextConfig: NextConfig = {
  turbopack: {
    // options
  },
}

export default nextConfig

Opting Out of Turbopack

If you need to continue using Webpack:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build --webpack",
    "start": "next start"
  }
}

Breaking Changes

1. Async Request APIs (Affects ~95% of Apps)

This is the single biggest breaking change. In Next.js 15, params, searchParams, cookies(), headers(), and draftMode() returned values directly. In Next.js 16, they return promises.

Before (Next.js 15)

export default function Page({ params, searchParams }) {
  const slug = params.slug
  const query = searchParams.q
  const cookieStore = cookies()
  return <div>{slug} - {query}</div>
}

After (Next.js 16)

export default async function Page({ params, searchParams }) {
  const { slug } = await params
  const { q } = await searchParams
  const cookieStore = await cookies()
  return <div>{slug} - {q}</div>
}

Use the codemod to migrate:

npx @next/codemod@canary upgrade latest

2. Node.js 20.9+ Required

Next.js 16 requires Node.js 20.9 or later. Node.js 18 is no longer supported.

# Check your Node version
node --version

3. Middleware Renamed to Proxy

The middleware.ts file is now proxy.ts:

# Rename your middleware file
mv middleware.ts proxy.ts
// Before (Next.js 15)
export function middleware(request: Request) {}

// After (Next.js 16)
export function proxy(request: Request) {}

Configuration flags are also renamed:

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  // Before: skipMiddlewareUrlNormalize
  skipProxyUrlNormalize: true,
}

export default nextConfig

4. Image Component Changes

Several defaults have changed:

  • minimumCacheTTL default changed from 60 seconds to 4 hours (14400 seconds)
  • 16 removed from default imageSizes array
  • qualities default changed to only [75]
  • Local IP optimization blocked by default
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  images: {
    // Restore previous behavior if needed
    minimumCacheTTL: 60,
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    qualities: [50, 75, 100],
    dangerouslyAllowLocalIP: true, // Only for private networks
  },
}

export default nextConfig

New Features

Cache Components with "use cache"

Next.js 16 introduces explicit caching with the "use cache" directive:

// next.config.ts
const nextConfig = {
  cacheComponents: true,
}

export default nextConfig
'use cache'

async function MyComponent() {
  const data = await fetch('/api/users')
  return <div>{data}</div>
}

New Caching APIs

'use server'

import { revalidateTag, updateTag } from 'next/cache'

// revalidateTag - for content where slight delay is acceptable
export async function updateArticle(articleId: string) {
  revalidateTag(`article-${articleId}`, 'max')
}

// updateTag - for immediate updates (read-your-writes)
export async function updateUserProfile(userId: string, profile: Profile) {
  await db.users.update(userId, profile)
  updateTag(`user-${userId}`)
}

React 19.2 Integration

Next.js 16 ships with React 19.2, including:

  • View Transitions: Animate elements during navigation
  • useEffectEvent: Extract non-reactive logic from Effects
  • Activity: Render background UI with display: none while maintaining state

React Compiler (Stable)

The React Compiler is now stable and automatically memoizes components:

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  reactCompiler: true,
}

export default nextConfig
npm install -D babel-plugin-react-compiler

Enhanced Routing and Navigation

  • Layout deduplication: Shared layouts downloaded once
  • Incremental prefetching: Only prefetch parts not already in cache

Parallel Routes Requirement

All parallel route slots now require explicit default.js files:

// app/@modal/default.tsx
import { notFound } from 'next/navigation'

export default function Default() {
  notFound()
}

// Or return null
export default function Default() {
  return null
}

Removed Features

  • AMP Support: All AMP APIs and configurations removed
  • next lint Command: Use ESLint directly
  • Runtime Configuration: serverRuntimeConfig and publicRuntimeConfig removed
  • next/legacy/image: Use next/image instead
  • images.domains: Use images.remotePatterns instead

Migrating Runtime Configuration

Before (Next.js 15)

// next.config.js
module.exports = {
  serverRuntimeConfig: {
    dbUrl: process.env.DATABASE_URL,
  },
  publicRuntimeConfig: {
    apiUrl: '/api',
  },
}

After (Next.js 16)

// For server-only values
async function fetchData() {
  const dbUrl = process.env.DATABASE_URL
  return await db.query(dbUrl, 'SELECT * FROM users')
}

// For client-accessible values, use NEXT_PUBLIC_ prefix
// .env.local
// NEXT_PUBLIC_API_URL="/api"

'use client'
export default function ClientComponent() {
  const apiUrl = process.env.NEXT_PUBLIC_API_URL
  return <p>API URL: {apiUrl}</p>
}

Migration Checklist

  1. ✅ Create a new branch for the upgrade
  2. ✅ Update Node.js to 20.9+
  3. ✅ Run the codemod: npx @next/codemod@canary upgrade latest
  4. ✅ Update all async API usages (params, searchParams, cookies, headers)
  5. ✅ Rename middleware.ts to proxy.ts
  6. ✅ Update Turbopack configuration to top-level
  7. ✅ Add default.js files to parallel routes
  8. ✅ Review and update image configuration
  9. ✅ Remove deprecated features (AMP, runtime config)
  10. ✅ Test thoroughly in staging before production

Conclusion

Next.js 16 brings significant performance improvements and a more explicit approach to caching. While there are breaking changes that require attention, the automated codemods handle most of the migration work. The performance gains from Turbopack alone make the upgrade worthwhile for most projects.

"

"The best way to predict the future is to create it." - Peter Drucker

Start your migration today and enjoy faster builds, better developer experience, and more control over your application's caching behavior!

Next.js 16 vs 15: Complete Upgrade Guide - Hex2077 Starter