Introduction
Next.js is a popular React framework for optimized web application development. The latest version, Next.js 15, brings various enhancements and new features that make it more robust and efficient.
New Features and Enhancements
React - React 19 RC (Release Candidate)
Next.js 15 RC now integrates with React 19 RC, offering support for new features such as Actions on both client and server, while leveraging React's experimental compiler and hydration error improvements.
React - React Compiler (Experimental)
Next.js 15 introduces support for the React Compiler, an experimental tool by the React team at Meta, which automatically optimizes code by leveraging deep understanding of plain JavaScript semantics and React rules, reducing the need for manual memoization and simplifying code maintenance.
Install babel-plugin-react-compiler
:
npm install babel-plugin-react-compiler
To incorporate the experimental.reactCompiler
option into your next.config.ts
file, you would do something like this:
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
module.exports = nextConfig;
If you prefer to configure the compiler to run in “opt-in” mode, where it's activated explicitly for specific files or components, you can do so as follows in your next.config.ts:
const nextConfig = {
experimental: {
reactCompiler: {
compilationMode: 'annotation',
},
},
};
module.exports = nextConfig;
Hydration error improvements
Next.js 15 builds upon the improvements made in error messaging and hydration errors introduced in Next.js 14.1 by enhancing the hydration error view. Now, hydration errors provide clearer visibility by displaying the source code of the error along with suggestions for resolving the issue.
An example of a previous hydration error message in Next.js 14.1 might have looked like:

In Next.js 15 RC, this hydration error message has been enhanced to provide more detailed information, possibly like:

Caching updates
To manage caching behavior in Next.js, including for fetch requests, GET Route Handlers, and client navigations, the framework utilizes the Web fetch API cache option. This option allows configuration of how server-side fetch requests interact with Next.js's persistent HTTP cache.
fetch('https://...', { cache: 'force-cache' | 'no-store' });
no-store
- fetches a resource from a remote server on every request and does not update the cache.force-cache
- fetches a resource from the cache (if it exists) or a remote server and updates the cache.
In Next.js 14, force-cache
was the default behavior if a cache option was not provided, except when a dynamic function or dynamic config option was used. However, in Next.js 15, no-store
is now the default behavior if a cache option is not provided. This change means that fetch requests will not be cached by default.
You can still enable caching for fetch requests in Next.js 15 by:
- Specifying the cache option as
force-cache
in individual fetch calls. - Designating the dynamic route config option as
force-static
for a specific route. - Utilizing the fetchCache route config option as
default-cache
to ensure that all fetch requests in a Layout or Page utilize force-cache unless they explicitly define their own cache option.
GET
Route Handlers are no longer cached by default
In Next.js 14, GET route handlers were cached by default unless they employed dynamic functions or configurations. However, in Next.js 15, GET functions are not cached by default. Special route handlers such as sitemap.ts, opengraph-image.tsx, and icon.tsx remain static by default, unless dynamic functions or configurations are utilized.
Client Router Cache no longer caches Page components by default
In Next.js 14.2.0, the experimental staleTimes flag was introduced to customize the Router Cache. In Next.js 15, while this flag remains accessible, the default behavior for Page segments is altered to have a staleTime of 0. This ensures that when navigating the app, the client consistently displays the latest data from the active Page component(s).
However, certain behaviors remain consistent:
- Shared layout data is not re-fetched from the server to support partial rendering.
- Back/forward navigation continues to restore from cache to maintain scroll position.
- Loading.js remains cached for 5 minutes (or as configured).
You can revert to the previous Client Router Cache behavior by adjusting the configuration accordingly:
const nextConfig = {
experimental: {
staleTimes: {
dynamic: 30,
},
},
};
module.exports = nextConfig;
Incremental adoption of Partial Prerendering (Experimental)
In Next.js 14, Partial Prerendering (PPR) was introduced as an optimization method that combines static and dynamic rendering on the same page.
By default, Next.js uses static rendering unless dynamic functions like cookies(), headers(), and uncached data requests are used. These APIs enable an entire route to opt into dynamic rendering. With PPR, you can enclose any dynamic UI within a Suspense boundary. When a new request arrives, Next.js promptly serves a static HTML shell, followed by rendering and streaming the dynamic parts within the same HTTP request.
To facilitate incremental adoption, an experimental_ppr
route config option has been introduced in Next.js. This option allows specific Layouts and Pages to opt into PPR.
import { Suspense } from "react"
import { StaticComponent, DynamicComponent } from "@/app/ui"
export const experimental_ppr = true
export default function Page() {
return {
<>
<StaticComponent />
<Suspense fallback={...}>
<DynamicComponent />
</Suspense>
</>
};
}
To utilize the new option, you'll have to configure the experimental.ppr
setting in your next.config.ts
file to 'incremental'
:
const nextConfig = {
experimental: {
ppr: 'incremental',
},
};
module.exports = nextConfig;
Once all segments have PPR enabled, it will be considered safe to set the ppr value to true, enabling it for the entire app and all future routes.
next/after
(Experimental)
When handling a user request, the server typically focuses on tasks directly related to generating the response. However, there are often additional tasks such as logging, analytics, and synchronizing with external systems that need to be performed.
Since these tasks are not directly related to generating the response, it's preferable for the user not to have to wait for them to be completed. Defering these tasks until after responding to the user presents a challenge because serverless functions halt computation immediately after closing the response.
The new experimental API called after()
addresses this issue by enabling you to schedule tasks to be executed after the response has finished streaming. This allows secondary tasks to run without blocking the primary response.
To use after(), simply add experimental.after
to your next.config.ts
file:
const nextConfig = {
experimental: {
after: true,
},
};
module.exports = nextConfig;
After adding the experimental.after
configuration to your next.config.ts
file, you can import the after()
function in your Server Components, Server Actions, Route Handlers, or Middleware to schedule tasks to be executed after the response has finished streaming:
import { unstable_after as after } from 'next/server';
import { log } from '@/app/utils';
export default function Layout({ children }) {
// Secondary task
after(() => {
log();
});
// Primary task
return <>{children}</>;
}
create-next-app
updates
In Next.js 15, create-next-app has been updated with a new design.

When you run create-next-app
, you'll encounter a new prompt asking whether you'd like to enable Turbopack for local development, with the default option set to No
.
✔ Would you like to use Turbopack for next dev? … No / Yes
You can use the --turbo flag to enable Turbopack.
npx create-next-app@rc --turbo
For easier project setup, a new --empty flag has been introduced to the CLI. This flag removes any unnecessary files and styles, providing a minimal "hello world" page.
npx create-next-app@rc --empty
Bundling external packages (Stable)
Bundling external packages can enhance the cold start performance of your application. By default, in the App Router, external packages are bundled, and you can exclude specific packages using the new serverExternalPackages configuration option.
In the Pages Router, external packages are not bundled by default. However, you can specify a list of packages to bundle using the existing transpilePackages option. With this configuration, you need to explicitly list each package.
To streamline configuration between the App and Pages Router, a new option, bundlePagesRouterDependencies, has been introduced to match the default automatic bundling of the App Router. Subsequently, you can utilize serverExternalPackages to exclude specific packages, if necessary.
const nextConfig = {
// Automatically bundle external packages in the Pages Router:
bundlePagesRouterDependencies: true,
// Opt specific packages out of bundling for both App and Pages Router:
serverExternalPackages: ['package-name'],
};
module.exports = nextConfig;
Upgrading from 14 to 15
To upgrade to Next.js version 15, execute the provided command with your chosen package manager. Please note that the minimum required versions for react
and react-dom
are now 19.
npm i next@rc react@rc react-dom@rc eslint-config-next@rc
yarn add next@rc react@rc react-dom@rc eslint-config-next@rc
pnpm up next@rc react@rc react-dom@rc eslint-config-next@rc
bun add next@rc react@rc react-dom@rc eslint-config-next@rc
Conclusion
Next.js 15 represents a significant leap forward in modern web development. With optimized performance and full support for React 19, Next.js 15 promises a better development experience and faster applications.
Personal Opinion
Next.js 15 demonstrates Vercel commitment to advancing web technology. I believe these new features will greatly benefit developers and propel web applications to the next level.
Future Direction
I hope Next.js continues to evolve with more features that streamline development and enhance application performance.
Thank you for reading! If you enjoyed this post, please like or react positively. Feel free to share your feedback, suggestions, or open a discussion on this topic.
If you’d like to support my work, consider donating for a coffee.