Next.js 15 in Production: Turbopack, React 19, and Breaking Changes

Next.js 15 is production-ready with Turbopack delivering 10x faster builds and React 19 support. Understand the caching behavior changes, Async Request APIs, and how Partial Prerendering combines static and dynamic rendering for optimal performance.

Adrian Chromenko
April 8, 2026
14 min read

Next.js 15: Production-Ready Performance

Next.js 15, released in late 2024 and refined through 2025-2026, represents the most significant performance leap in the framework's history. Turbopack is now stable, React 19 is fully supported, and new rendering strategies like Partial Prerendering are changing how we build web applications.

What's New in Next.js 15

  • Turbopack (stable) - 10x faster local dev, 5x faster production builds
  • React 19 support - Server Components, Actions, and automatic memoization
  • Partial Prerendering - Mix static and dynamic content in one page
  • Async Request APIs - Better handling of request-specific data
  • Caching changes - More predictable, opt-in caching behavior
  • Improved error handling - Better DX with clearer error messages

Turbopack: The Build Tool Revolution

Turbopack, built in Rust, replaces Webpack as Next.js's bundler. The performance gains are dramatic:

Benchmarks

  • Cold start: 10x faster than Webpack (1.2s vs 12.8s for large apps)
  • Hot Module Replacement: Updates reflect in under 50ms
  • Production builds: 5x faster (2 min vs 10 min for enterprise apps)

Enabling Turbopack

In development (now default in Next.js 15):

npm run dev

For production builds:

// next.config.js
module.exports = {
  experimental: {
    turbo: {
      // Turbopack config options
    }
  }
}

Migration Notes

Most Webpack plugins have Turbopack equivalents. Check compatibility:

  • Fully supported: CSS modules, PostCSS, TypeScript, image optimization
  • Partially supported: Some Webpack loaders (check documentation)
  • Not supported: Custom Webpack configurations (requires rewrite)

React 19 Integration

Next.js 15 is built specifically for React 19, taking full advantage of Server Components, Actions, and the React Compiler.

Server Actions

Server Actions simplify form handling and data mutations:

// app/actions.js
'use server';

export async function createPost(formData) {
  const title = formData.get('title');
  const content = formData.get('content');

  await db.posts.insert({ title, content });
  revalidatePath('/blog');
  redirect('/blog');
}

// app/create-post/page.jsx
import { createPost } from './actions';

export default function CreatePostPage() {
  return (
    <form action={createPost}>
      <input name="title" required />
      <textarea name="content" required />
      <button type="submit">Publish</button>
    </form>
  );
}

No API routes, no useState, no fetch calls. React handles everything.

Server Components by Default

All components in the app directory are Server Components unless marked with 'use client':

// app/blog/[slug]/page.jsx
// This is a Server Component (default)
import { db } from '@/lib/db';

export default async function BlogPost({ params }) {
  const post = await db.posts.findBySlug(params.slug);

  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

Database queries run on the server. No client-side JavaScript is sent for this component.

Partial Prerendering (PPR)

PPR is Next.js 15's most innovative feature. It combines static and dynamic rendering in a single page, delivering instant static shells while streaming dynamic content.

How It Works

  1. Static parts of the page (layout, navigation) are prerendered at build time
  2. Dynamic parts (user-specific content, real-time data) stream in when requested
  3. User sees instant static content, then dynamic sections hydrate progressively

Example

// app/dashboard/page.jsx
import { Suspense } from 'react';
import StaticHeader from './StaticHeader'; // Prerendered
import UserProfile from './UserProfile'; // Dynamic
import RecentActivity from './RecentActivity'; // Dynamic

export default function Dashboard() {
  return (
    <>
      <StaticHeader /> {/* Served instantly from static cache */}

      <Suspense fallback={<ProfileSkeleton />}>
        <UserProfile /> {/* Streamed dynamically */}
      </Suspense>

      <Suspense fallback={<ActivitySkeleton />}>
        <RecentActivity /> {/* Streamed dynamically */}
      </Suspense>
    </>
  );
}

Enabling PPR

// next.config.js
module.exports = {
  experimental: {
    ppr: true
  }
}

Async Request APIs

Next.js 15 makes request-specific data (headers, cookies, params) async to work better with React's streaming and Suspense.

Breaking Change

In Next.js 14:

import { cookies } from 'next/headers';

export default function Page() {
  const token = cookies().get('token');
  return <div>{token}</div>;
}

In Next.js 15:

import { cookies } from 'next/headers';

export default async function Page() {
  const cookieStore = await cookies();
  const token = cookieStore.get('token');
  return <div>{token}</div>;
}

All request APIs are now async:

  • cookies()
  • headers()
  • params (in page.jsx)
  • searchParams (in page.jsx)

Migration Codemod

Next.js provides automated migration:

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

Caching Behavior Changes

Next.js 15 changes caching from opt-out to opt-in for more predictable behavior.

New Defaults

  • fetch() requests: No longer cached by default
  • Route handlers: No longer cached by default
  • Client-side Router Cache: No longer caches Pages (only shared layouts)

Opt-In to Caching

// Cache a specific fetch request
const data = await fetch('https://api.example.com/data', {
  cache: 'force-cache' // or cache: 'no-store'
});

// Cache a route handler
export const dynamic = 'force-static';

// Or cache for specific duration
export const revalidate = 3600; // Cache for 1 hour

Why This Change?

Developers were confused by overly aggressive default caching. The new opt-in approach makes caching explicit and predictable.

Improved Error Handling

Better Error Messages

Next.js 15 provides clearer error messages with:

  • Stack traces that point to actual source code (not compiled output)
  • Suggestions for fixing common errors
  • Links to relevant documentation

Error Boundaries

Enhanced error boundary behavior:

// app/dashboard/error.jsx
'use client';

export default function Error({ error, reset }) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>Try again</button>
    </div>
  );
}

Performance Optimizations

Metadata API Improvements

Generate SEO metadata efficiently:

// app/blog/[slug]/page.jsx
export async function generateMetadata({ params }) {
  const post = await db.posts.findBySlug(params.slug);

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      images: [post.image]
    }
  };
}

Image Optimization

Enhanced next/image with better defaults:

  • Automatic format detection (WebP, AVIF)
  • Improved placeholder generation
  • Better lazy loading behavior

Migration Guide

From Next.js 14 to 15

  1. Update dependencies:
npm install next@latest react@latest react-dom@latest
  1. Run codemods:
npx @next/codemod@latest next-async-request-api .
npx @next/codemod@latest next-response-cookies .
  1. Update caching: Review and update fetch() calls to explicitly set cache behavior
  2. Test thoroughly: Caching changes may affect application behavior
  3. Enable Turbopack: Should work out of the box, but test custom Webpack configs

Common Issues

  • Async params/searchParams: Add await before accessing
  • Missing cache hits: Add explicit cache: 'force-cache' to fetch calls
  • Webpack plugin errors: Check Turbopack compatibility, may need rewrites

Production Deployment

Vercel (Recommended)

Vercel provides first-class support for Next.js 15 features:

  • Automatic Turbopack builds
  • Edge runtime support for Server Actions
  • Optimized CDN for static assets
  • Built-in analytics for Core Web Vitals

Self-Hosted

For self-hosting:

npm run build
npm start

Requirements:

  • Node.js 18.17 or later
  • Sufficient memory for build process (2GB+ for large apps)
  • CDN for static assets (Cloudflare, AWS CloudFront)

Best Practices

  • Use Server Components by default: Only add 'use client' when necessary
  • Leverage Server Actions: Simplify mutations, eliminate API routes
  • Implement PPR: Combine static and dynamic for best performance
  • Explicitly define caching: Don't rely on defaults, be intentional
  • Monitor performance: Use Vercel Analytics or custom monitoring to track Web Vitals

Conclusion

Next.js 15 delivers transformational performance improvements through Turbopack, embraces React 19's Server Components and Actions, and introduces innovative rendering strategies like Partial Prerendering.

The caching changes require intentional migration, but result in more predictable behavior. Async Request APIs align better with React's streaming architecture, though they're a breaking change requiring code updates.

For new projects, Next.js 15 is the obvious choice. For existing projects, the migration effort is justified by the 5-10x build performance improvements and better developer experience.

Tags

Next.js 15TurbopackReact 19

Related Articles

React 19 Performance: The Automatic Optimization Revolution
React12 min read

React 19 Performance: The Automatic Optimization Revolution

React 19's compiler eliminates 70% of manual optimizations. Discover how Server Components reduce bundle sizes by 50%, why INP replaced FID for performance measurement, and how architectural changes deliver 40-60% faster page loads without touching client code.

Adrian Chromenko

Ready to Start Your Project?

Let's discuss how we can help bring your vision to life with our expert development services.

Contact Us

More Articles

React 19 Performance: The Automatic Optimization Revolution

React 19 Performance: The Automatic Optimization Revolution

Recent
12 min read

Article Tags

Next.js 15TurbopackReact 19