Web Development

Next.js 15 App Router: Critical Changes Explained

The Debuggers Engineering Team
11 min read

TL;DR

  • The biggest breaking change: fetch() no longer caches by default. If your app relied on automatic request deduplication, it will make more API calls after upgrading
  • params and searchParams are now async Promises, requiring await in page/layout components
  • Turbopack is stable and delivers 50-70% faster dev server startup, but production builds still use Webpack
  • React 19 support enables Server Actions improvements, useActionState, and the use() hook natively

Table of Contents

Next.js web development with React code on a modern display

The Caching Overhaul

This is the change that broke the most applications during upgrade.

In Next.js 14, fetch() in Server Components cached responses by default. If three different components on the same page fetched the same URL, only one network request was made. This was great for performance but surprising for developers who did not realise caching was happening.

Next.js 15 changed the default: fetch() no longer caches unless you explicitly opt in.

Before (Next.js 14):

// This was cached by default - subsequent calls returned cached data
const data = await fetch('https://api.example.com/users');

After (Next.js 15):

// No caching by default - every call hits the network
const data = await fetch('https://api.example.com/users');

// Opt into caching explicitly
const data = await fetch('https://api.example.com/users', {
  cache: 'force-cache'
});

// Or use Next.js's revalidation
const data = await fetch('https://api.example.com/users', {
  next: { revalidate: 3600 } // Cache for 1 hour
});

Why This Matters

If your Next.js 14 app made 5 identical API calls per page render and relied on automatic deduplication, upgrading to Next.js 15 means 5 actual network requests per render. This can:

  1. Increase API costs if you pay per request
  2. Slow down page loads with redundant network roundtrips
  3. Hit rate limits on third-party APIs

The fix is to audit your fetch() calls and add explicit caching where needed. Use cache: 'force-cache' for truly static data and next: { revalidate: N } for data that changes periodically.

Web application performance dashboard showing caching metrics

Async Params and SearchParams

In Next.js 14, params and searchParams were synchronous objects available directly in page components:

Before:

// Next.js 14
export default function Page({ params }: { params: { slug: string } }) {
  return <div>{params.slug}</div>;
}

After:

// Next.js 15
export default async function Page({ 
  params 
}: { 
  params: Promise<{ slug: string }> 
}) {
  const { slug } = await params;
  return <div>{slug}</div>;
}

This applies to generateMetadata, generateStaticParams return value usage, and layout components. Every file that accesses params or searchParams needs this change.

The migration is mechanical but tedious. For a large app with 50+ routes, expect 1-2 hours of find-and-replace work. Next.js provides a codemod to automate most of it:

npx @next/codemod@latest next-async-request-api .

Turbopack is Stable

Turbopack, the Rust-based bundler that Vercel has been developing since 2022, graduated from beta in Next.js 15.

What changed for developers:

  • next dev --turbopack is now the recommended way to start the dev server
  • Dev server cold start is 50-70% faster than Webpack
  • Hot Module Replacement (HMR) updates are 2-5x faster
  • Memory usage is lower for large projects

What did NOT change:

  • Production builds (next build) still use Webpack by default
  • Turbopack for production is available as an opt-in flag but not yet recommended
  • Some Webpack plugins do not have Turbopack equivalents

Real Build Time Comparison

Project SizeWebpack Cold StartTurbopack Cold StartImprovement
Small (20 routes)3.2s1.4s56% faster
Medium (100 routes)8.5s3.1s64% faster
Large (500+ routes)22s7.8s65% faster

The improvement is most noticeable on larger projects where Webpack's single-threaded bottleneck was most painful.

React 19 Support

Next.js 15 ships with React 19, which brings several features to the App Router:

useActionState hook: Replaces the useFormState pattern for Server Actions:

'use client';
import { useActionState } from 'react';
import { createUser } from './actions';

export function CreateUserForm() {
  const [state, action, isPending] = useActionState(createUser, null);

  return (
    <form action={action}>
      <input name="email" type="email" />
      <button disabled={isPending}>
        {isPending ? 'Creating...' : 'Create User'}
      </button>
      {state?.error && <p>{state.error}</p>}
    </form>
  );
}

use() hook: Read promises and context in render:

import { use } from 'react';

function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise); // Suspends until resolved
  return <div>{user.name}</div>;
}

Document metadata in components: <title>, <meta>, and <link> tags can now be rendered from any component and are hoisted to <head> automatically.

Step-by-Step Migration Guide

  1. Update dependencies:
npm install next@15 react@19 react-dom@19
  1. Run the async request API codemod:
npx @next/codemod@latest next-async-request-api .
  1. Audit fetch caching: Search for all fetch() calls. Add explicit cache or next.revalidate options where automatic caching was relied upon.

  2. Update TypeScript types: The params type in page/layout components changed from { params: T } to { params: Promise<T> }.

  3. Test thoroughly: Run your full test suite. Pay special attention to pages that fetch data, as the caching behaviour change will surface as unexpected extra API calls.

  4. Enable Turbopack: Add --turbopack to your dev script:

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

When debugging your API routes during migration, use our free API Request Tester to verify your endpoints still return expected responses. Our JSON formatter helps validate the response payloads.

Is the App Router Mature Enough

The honest answer in 2026: yes, for most applications.

The App Router has stabilised significantly. Server Components work reliably. Server Actions handle form submissions and mutations well. The mental model of server-first with client opt-in is well-documented and understood by the community.

Remaining rough edges:

  • Error boundaries in nested layouts can be tricky to configure correctly
  • Parallel routes are powerful but have a steep learning curve
  • Intercepting routes (the (.) convention) still feel experimental in complex navigation patterns
  • Client-side navigation occasionally shows stale cached data when Server Components re-render

For a comparison of Server Components vs Client Components, see our post on React Server Components vs Client Components.

Developer migrating a Next.js application to the latest version

Performance: What Got Faster and What Got Slower

Faster:

  • Dev server startup with Turbopack (50-70% faster)
  • HMR updates (2-5x faster)
  • Server Action execution (React 19 optimisations)
  • Static page generation at build time (incremental improvements)

Slower (or perceived as slower):

  • Pages that relied on automatic fetch caching may feel slower because they now make more network requests
  • Initial bundle size increased slightly due to React 19
  • Cold builds with Webpack remain approximately the same speed

Before and After Code Examples

Dynamic route page:

// Before (Next.js 14)
export default function BlogPost({ params }: { params: { slug: string } }) {
  return <h1>{params.slug}</h1>;
}

// After (Next.js 15)
export default async function BlogPost({ 
  params 
}: { 
  params: Promise<{ slug: string }> 
}) {
  const { slug } = await params;
  return <h1>{slug}</h1>;
}

Data fetching with caching:

// Before (Next.js 14) - cached by default
const res = await fetch('https://api.example.com/data');

// After (Next.js 15) - explicit caching required
const res = await fetch('https://api.example.com/data', {
  next: { revalidate: 3600 }
});

This site, built by The Debuggers, runs on Next.js and serves as an example of the App Router in production with optimised Core Web Vitals.

Frequently Asked Questions

Should I upgrade to Next.js 15 or wait?

Upgrade if you are starting a new project or have good test coverage on your existing project. The caching change is the biggest risk during migration, and having tests that verify data fetching behaviour will catch regressions early. If your project has minimal test coverage and relies heavily on implicit fetch caching, invest in tests first before upgrading.

Will my Next.js 14 App Router code work in 15?

Most of it will work without changes. The two breaking changes that require code modification are the async params/searchParams (use the codemod to fix) and the fetch caching default (audit your fetch calls). Components, layouts, and routing patterns are backward compatible. Third-party libraries that depend on React 18 APIs may need updates for React 19.

Is Turbopack ready for production builds?

As of Next.js 15, Turbopack is stable for the development server but still experimental for production builds. Use next dev --turbopack for development and the default Webpack for next build. Vercel targets full Turbopack production support in a future release. The dev server alone is worth the upgrade for the speed improvement.

Does Next.js 15 solve the App Router's complexity?

Not entirely. The App Router's mental model remains complex, with server components, client components, server actions, parallel routes, and intercepting routes creating a steep learning curve. Next.js 15 stabilises these features and improves documentation, but does not simplify the underlying concepts. Teams need time to build familiarity with the patterns.


Debugging your Next.js API routes?

Use our free API Request Tester to test your endpoints, inspect response headers, and validate JSON payloads. All processing happens in your browser with zero server-side transmission.

Building a Next.js application and need expert guidance? The Debuggers offers Next.js development and consulting services.

Need Help Implementing This in a Real Project?

Our team supports end-to-end development for web and mobile software, from architecture to launch.

nextjs 15 app routernextjs 15 featuresnextjs app router migrationnextjs 15 changesnextjs caching changes

Found this helpful?

Join thousands of developers using our tools to write better code, faster.