Next.js 15: Full Feature Breakdown, Breaking Changes & Upgrade Guide

Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

July 17, 2024 Β· 6 min read

Next.js 15 - release date

Next.js 15 was released on October 21, 2024, at Next.js Conf 2024. It ships the most significant behavioral change in the framework's history: caching is now uncached by default. This guide covers every feature that shipped in stable form, async Request APIs, the new <Form> component, stable Turbopack development mode, instrumentation.js, Server Actions security, next.config.ts TypeScript support, and all breaking changes to handle before upgrading.

The biggest breaking change: Async Request APIs

The most impactful change in Next.js 15 is that all request-scoped APIs are now async. In Next.js 14, you could call cookies(), headers(), draftMode(), params, and searchParams synchronously. In Next.js 15, every one of these must be awaited.

The reason for this change: it enables Next.js to prepare work before a request arrives, which opens the door to more aggressive prerendering and caching optimizations in future versions.

// Next.js 14 β€” synchronous (no longer works)
import { cookies } from 'next/headers'

export default function Page() {
  const cookieStore = cookies()
  const token = cookieStore.get('token')
  return <div>{token?.value}</div>
}
// Next.js 15 β€” async required
import { cookies } from 'next/headers'

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

The same pattern applies to headers(), draftMode(), and the params/searchParams props in layouts and pages:

// Next.js 15 β€” params and searchParams are now async
export default async function ProductPage({
  params,
  searchParams,
}: {
  params: Promise<{ id: string }>
  searchParams: Promise<{ sort: string }>
}) {
  const { id } = await params
  const { sort } = await searchParams
  return <div>Product {id} sorted by {sort}</div>
}

Run the automated codemod to migrate all call sites at once:

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

Caching is now uncached by default

This is the change most likely to break existing Next.js 14 applications silently. In Next.js 14, fetch requests were cached by default. In Next.js 15, they are not, every fetch call is treated as dynamic unless you explicitly opt in to caching.

Three things are now uncached by default:

  • fetch requests (previously cached unless you added cache: "no-store")

  • GET Route Handlers (previously cached by default)

  • Client Router Cache page segments (staleTime is now 0 β€” page data is always re-fetched on navigation)

To opt back in to caching where you need it:

// Cache a single fetch call
const data = await fetch('/api/products', { cache: 'force-cache' })

// Cache an entire route segment
export const dynamic = 'force-static'

// Cache all fetches in a layout or page
export const fetchCache = 'default-cache'

React 19 support

Next.js 15 ships with React 19 support for the App Router. The Pages Router retains React 18 backward compatibility, you can upgrade Next.js to 15 without upgrading React if your app uses the Pages Router.

  • App Router: React 19 stable (includes useActionState, useOptimistic, Server Actions, and more)

  • Pages Router: React 18 remains compatible - upgrade independently

  • React Compiler: available as experimental (reactCompiler: true in next.config.ts)

  • Hydration error messages now show a source code diff of what the server rendered vs. what the client expected

Turbopack Dev is now stable

next dev --turbo is stable in Next.js 15. The performance gains compared to the webpack-based dev server are substantial:

  • 76.7% faster local server startup

  • 96.3% faster Fast Refresh (measured on vercel.com)

  • 45.8% faster initial route compile

Note: Turbopack in Next.js 15 is stable for development only. Production Turbopack builds (next build --turbo) are stable in Next.js 16.

The new <Form> component

Next.js 15 introduces next/form, a form component that extends the native HTML <form> with three automatic behaviors:

  • Prefetches the destination layout and loading UI when the form enters the viewport

  • Client-side navigation on submit (preserves shared layouts, no full page reload)

  • Progressive enhancement (works without JavaScript)

import Form from 'next/form'

export default function SearchForm() {
  return (
    <Form action="/search">
      <input name="q" placeholder="Search..." />
      <button type="submit">Search</button>
    </Form>
  )
}

On submission, the query string is appended to the action URL and the user navigates client-side to /search?q=... without a full page reload.

unstable_after() for post-response work

unstable_after() lets you execute code after a response has finished streaming, without blocking the response. This is the right place for analytics tracking, audit logging, and external system syncs that should not add latency for the user.

Enable it in next.config.ts first:

const nextConfig = {
  experimental: {
    after: true,
  },
}
export default nextConfig
import { unstable_after as after } from 'next/server'
import { recordPageView } from '@/lib/analytics'

export default async function Page() {
  after(() => {
    // Runs after the response streams β€” does not block the user
    recordPageView()
  })

  return <div>Page content</div>
}

instrumentation.js is now stable

instrumentation.js (or instrumentation.ts) is a special file at the root of your project that Next.js calls once when the server starts. Use it to initialize observability providers (OpenTelemetry, Sentry, Datadog) and set up error tracking.

Next.js 15 adds the onRequestError hook, which fires for every server error with full request context, route, component type, and the error itself:

// instrumentation.ts
export function register() {
  // Initialize your observability provider once on server start
  Sentry.init({ dsn: process.env.SENTRY_DSN })
}

export function onRequestError(
  error: Error,
  request: { path: string; method: string },
  context: { routeType: string }
) {
  Sentry.captureException(error, {
    extra: { path: request.path, routeType: context.routeType },
  })
}

Server Actions security improvements

Next.js 15 ships two Server Actions security improvements that happen automatically with no configuration required:

  • Dead code elimination: Server Actions that are defined but never imported or called from a Client Component are removed from the client bundle entirely. Previously they could be exposed as callable endpoints even if unused.

  • Unguessable action IDs: Action IDs are now non-deterministic and rotated between builds. A URL observed in one build cannot be used to call a Server Action in a subsequent build.

next.config.ts TypeScript support

Next.js 15 supports next.config.ts natively. Rename your config file and use the NextConfig type for full autocomplete and type checking on every config option:

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  // Full autocomplete and type safety here
}

export default nextConfig

Static Route Indicator

Next.js 15 adds a visual indicator in development mode that shows whether the current route is rendered statically or dynamically. This makes it easy to verify that a route you expect to be static has not accidentally become dynamic (for example, due to an uncached fetch call or a cookies() read). To disable it in config:

const nextConfig: NextConfig = {
  devIndicators: {
    appIsrStatus: false,
  },
}

Breaking changes summary

Change

What to do

Node.js 18.18.0 minimum

Upgrade Node.js if below 18.18.0

cookies(), headers(), draftMode() are now async

Await all calls; run next-async-request-api codemod

params and searchParams are now async

Await in layouts and pages; type as Promise<{ key: string }>

squoosh removed from next/image

Install sharp for image optimization

@next/font external package removed

Use next/font (built-in since Next.js 13)

fetch is uncached by default

Audit all fetch calls; add cache: "force-cache" where caching is needed

ssr: false not allowed in Server Components with next/dynamic

Move dynamic import to a Client Component

Should you upgrade to Next.js 15 or go straight to 16?

If you are currently on Next.js 14 and starting an upgrade today, go straight to Next.js 16. The migration effort is similar and you get additional improvements: stable Turbopack production builds, Cache Components with the "use cache" directive, proxy.ts, and React 19.2.

If you are already on Next.js 15, the upgrade to 16 is worthwhile and the codemod handles most of the breaking changes. The key things to address manually: revalidateTag() now requires a second argument, and middleware.ts should be renamed to proxy.ts.

How to upgrade to Next.js 15

Run the automated codemod first, it handles async Request API migrations and most other breaking changes:

npx @next/codemod@canary upgrade latest

To upgrade manually:

npm install next@15 react@19 react-dom@19

Bring Your Ideas to Life πŸš€

If you need help with a Next.js project let's get in touch.

Lucky Media is proud to be recognized as a leading Next.js Development Agency

FAQs

Is Next.js 15 stable?

Yes. Next.js 15 was released on October 21, 2024, at Next.js Conf 2024. It is stable and production-ready.

What is the biggest breaking change in Next.js 15?

The async Request APIs. In Next.js 15, cookies(), headers(), draftMode(), params, and searchParams are all async and must be awaited. Run the automated codemod (npx @next/codemod@canary next-async-request-api .) to migrate most of your codebase automatically.

Do I need React 19 to use Next.js 15?

No. The Pages Router in Next.js 15 is backward compatible with React 18. The App Router uses React 19. You can upgrade Next.js to 15 without changing your React version if you use the Pages Router.

Is Turbopack stable in Next.js 15?

Turbopack is stable for development (next dev --turbo) in Next.js 15. Production Turbopack builds (next build --turbo) became stable in Next.js 16.

Should I upgrade to Next.js 15 or go straight to Next.js 16?

If you are on Next.js 14 today, go straight to Next.js 16. The migration effort is similar to going to 15, and you get additional improvements including stable production Turbopack, Cache Components, and proxy.ts. If you are already on Next.js 15, upgrading to 16 is worthwhile and the codemod handles most of the work.

Technologies

Next.jsReact
Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

Founder and CEO of Lucky Media

Stay up-to-date

Be updated with all news, products and tips we share!

Let’s chat

We partner with a limited number of brands each quarter to ensure senior-level attention on every project.

lokman and arlind headshots
Teamwork