Next.js 15 App Router: Critical Changes Explained
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 upgradingparamsandsearchParamsare now async Promises, requiringawaitin 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 theuse()hook natively
Table of Contents
- The Caching Overhaul
- Async Params and SearchParams
- Turbopack is Stable
- React 19 Support
- Step-by-Step Migration Guide
- Is the App Router Mature Enough
- Performance: What Got Faster and What Got Slower
- Before and After Code Examples
- Frequently Asked Questions
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:
- Increase API costs if you pay per request
- Slow down page loads with redundant network roundtrips
- 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.
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 --turbopackis 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 Size | Webpack Cold Start | Turbopack Cold Start | Improvement |
|---|---|---|---|
| Small (20 routes) | 3.2s | 1.4s | 56% faster |
| Medium (100 routes) | 8.5s | 3.1s | 64% faster |
| Large (500+ routes) | 22s | 7.8s | 65% 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
- Update dependencies:
npm install next@15 react@19 react-dom@19
- Run the async request API codemod:
npx @next/codemod@latest next-async-request-api .
-
Audit fetch caching: Search for all
fetch()calls. Add explicitcacheornext.revalidateoptions where automatic caching was relied upon. -
Update TypeScript types: The
paramstype in page/layout components changed from{ params: T }to{ params: Promise<T> }. -
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.
-
Enable Turbopack: Add
--turbopackto 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.
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.
Found this helpful?
Join thousands of developers using our tools to write better code, faster.