Next.js 15 Stable Release: React 19, Turbopack, and the New Caching Model

Next.js 15 Stable Release: React 19, Turbopack, and the New Caching Model

The React 19 Framework

On October 21, 2024, Vercel released Next.js 15—the first major framework built around React 19's new primitives. This release integrates Server Actions, React Compiler, Partial Prerendering, and modernized caching into a cohesive full-stack framework.

Next.js 15 represents the culmination of React's "server-first" vision. With React 19 as its foundation, it treats the server as a first-class rendering environment rather than a deployment target.

Key Changes and New Features

Async Request APIs (Breaking Change)

The most significant breaking change: headers, cookies, params, and searchParams are now asynchronous:

tsx
1// Next.js 14 (synchronous)
2import { cookies } from 'next/headers';
3
4export default function Page() {
5  const cookieStore = cookies();
6  const theme = cookieStore.get('theme');
7  return <div>Theme: {theme?.value}</div>;
8}
9
10// Next.js 15 (asynchronous)
11import { cookies } from 'next/headers';
12
13export default async function Page() {
14  const cookieStore = await cookies();
15  const theme = cookieStore.get('theme');
16  return <div>Theme: {theme?.value}</div>;
17}
18
19// Similarly for params:
20// Before: function Page({ params }) { const slug = params.slug; }
21// After:  async function Page({ params }) { const { slug } = await params; }

This change enables streaming and partial rendering optimizations. A codemod (npx @next/codemod@canary upgrade latest) automates migration.

React Compiler (Experimental)

Next.js 15 integrates the React Compiler—an automatic optimization tool that eliminates the need for manual useMemo, useCallback, and React.memo:

tsx
1// Before React Compiler: manual memoization everywhere
2const MemoizedComponent = React.memo(({ items }) => {
3  const sortedItems = useMemo(() => 
4    items.sort((a, b) => a.name.localeCompare(b.name)), 
5    [items]
6  );
7  const handleClick = useCallback((id) => {
8    selectItem(id);
9  }, [selectItem]);
10  
11  return sortedItems.map(item => 
12    <Item key={item.id} onClick={handleClick} />
13  );
14});
15
16// After React Compiler: just write normal code
17const Component = ({ items }) => {
18  const sortedItems = items.sort((a, b) => 
19    a.name.localeCompare(b.name)
20  );
21  const handleClick = (id) => selectItem(id);
22  
23  return sortedItems.map(item => 
24    <Item key={item.id} onClick={handleClick} />
25  );
26};
27// Compiler automatically optimizes re-renders!

Enable it in next.config.ts:

typescript
1const nextConfig = {
2  experimental: {
3    reactCompiler: true,
4  },
5};

Caching Changes (Breaking)

Next.js 15 reverses the aggressive caching defaults that frustrated developers:

FeatureNext.js 14Next.js 15
fetch()Cached by defaultNot cached by default
GET Route HandlersCached by defaultNot cached by default
Client Router Cache5 min stale time0 sec (no stale)
Page/Layout dataCachedNot cached
tsx
1// Next.js 14: had to opt out of caching
2fetch(url, { cache: 'no-store' });  // Explicit no-cache
3
4// Next.js 15: fresh data by default, opt in to caching
5fetch(url);  // Not cached (fresh every request)
6fetch(url, { next: { revalidate: 3600 } });  // Opt-in to 1-hour cache

Partial Prerendering (PPR)

PPR combines static and dynamic rendering in a single page:

tsx
1import { Suspense } from 'react';
2
3export const experimental_ppr = true;
4
5export default function ProductPage() {
6  return (
7    <div>
8      {/* Static shell: pre-rendered at build time */}
9      <Header />
10      <ProductImages />
11      <ProductDescription />
12      
13      {/* Dynamic parts: streamed on request */}
14      <Suspense fallback={<PriceSkeleton />}>
15        <DynamicPrice />  {/* Real-time pricing */}
16      </Suspense>
17      
18      <Suspense fallback={<ReviewsSkeleton />}>
19        <UserReviews />  {/* Personalized reviews */}
20      </Suspense>
21    </div>
22  );
23}

The static shell loads instantly (from CDN), while dynamic parts stream in. Users see content immediately with progressive enhancement.

Turbopack Dev (Stable)

After years of development, Turbopack is now stable for development:

MetricWebpackTurbopackImprovement
Cold start8.2s1.4s5.8x faster
HMR (small change)450ms80ms5.6x faster
HMR (large change)1200ms190ms6.3x faster
Memory usage1.8GB0.9GB50% less

Enable with next dev --turbopack (or --turbo).

Server Actions Improvements

Server Actions get enhanced security and developer experience:

tsx
1'use server';
2
3import { revalidatePath } from 'next/cache';
4import { redirect } from 'next/navigation';
5
6export async function createPost(formData: FormData) {
7  // Server-side validation
8  const title = formData.get('title') as string;
9  const content = formData.get('content') as string;
10  
11  if (!title || title.length < 3) {
12    return { error: 'Title must be at least 3 characters' };
13  }
14  
15  // Database operation
16  await db.posts.create({ title, content });
17  
18  // Revalidate and redirect
19  revalidatePath('/blog');
20  redirect('/blog');
21}

Migration Guide

bash
1# Automatic upgrade
2npx @next/codemod@canary upgrade latest
3
4# Key migration steps:
5# 1. Update async request APIs (cookies, headers, params)
6# 2. Review caching behavior (now uncached by default)
7# 3. Test Server Actions
8# 4. Enable Turbopack for dev
9# 5. Optionally enable React Compiler

Impact on the Frontend Ecosystem

Next.js 15's integration of React 19 primitives cements the "server-first" paradigm in modern web development. The framework now handles the entire stack—from database queries to interactive UI—with a single, cohesive model.

Sources: Next.js 15 Blog, Next.js Documentation, React 19