Next.js 15: Unpacking the Latest Chapter in Full-Stack Development
Table of contents
A few days ago, Vercel unveiled the stable version of Next.js 15, following two previous release candidates (RC). The latest release isn't just another version bump or iteration—it's a reimagining of how we build modern web applications.
This major update introduces architectural changes and new features that will significantly improve and enhance your developer experience, application performance, and capabilities.
In the following sections, we will walk you through the major changes, such as enhanced caching, Async Request APIs and other impactful changes, that make this release noteworthy.
Prerequisites
You will need the knowledge of the following to complete this tutorial:
- React fundamentals
- Next.js basics
Here are the major new changes in Next.js 15
Upgrade to the latest Next.js and React versions
Next.js introduced some breaking changes that require small changes throughout your code. Thanks to a more enhanced codemod CLI, upgrading from the previous versions of Next.js to Next.js 15 is now simpler than ever. The codemod automatically handles all necessary changes to ensure a smooth transition by running a single command in your existing project.
The Codemod CLI upgrades React
, React DOM
, ESLint
version, Next.js
and other dependencies you used in your code simultaneously rather than upgrading each package separately. The Codemod CLI also modifies your existing project code to ensure compatibility with the latest Next.js version.
Instead of manually upgrading individual packages like this:
npm install next@latest react@rc react-dom@rc
# OR
npm i next@latest react@latest react-dom@latest eslint-config-next@latest
You can use the Codemod CLI. The Codemod CLI will then guide you through the upgrade process, ensuring your project is compatible with the latest versions of all dependencies.
# Use the new automated upgrade CLI
npx @next/codemod@canary upgrade latest
Before upgrading: package.json
After upgrading: package.json
Async Request APIs
The Async Request API change is one of the major changes made in Next.js 15.
In previous versions of Next.js, you could access request data like headers
, cookies
, params
, and searchParams
directly in your code, like this:
const headersValue = headers()
However, in the latest version of Next.js, these values are now returned as Promises.
To access the data, await the Promise:
const headersValue = await headers()
Note: During this transition period, synchronous access remains supported until the next Next.js release. Hence, you can still continue to access the request data synchronously until the release of Next.js 16.
Caching Semantics
Next.js’s default caching behaviour presented significant challenges. Previously, fetch
, GET
requests, and page components were automatically cached, often resulting in unexpected behaviour and complicated development workflows. Next.js 15 addresses the aggressive caching issues. The new changes reduce automatic caching, giving you more control over cached data. The new changes improve caching.
Note: If you need to maintain Next.js 14's caching behaviour for fetching data, set your caching preferences:
// Disable caching
fetch('https://api.example.com', { cache: 'no-store' })
// Enable aggressive caching (Next.js 14 behavior)
fetch('https://api.example.com', { cache: 'force-store' })
The GET
requests were cached in previous versions. However, in Next.js new release, the GET
requests in Route Handlers are no longer cached by default. You can still enable caching by setting export dynamic = 'force-static'
. However, special routes like sitemap.ts
, opengraph-image.tsx
, and icon.tsx
maintain their default static behaviour unless you configure them otherwise.
The Client Router Cache has also been updated. Normally, Page components were cached by default. Navigating to a previous page provides you with cached content. In Next.js 15, Page components fetch fresh data on each navigation, using a staleTime
of 0.
While these changes affect page component caching, the following core behaviours remain unchanged:
- shared layout data stays cached to support partial rendering
- back/forward navigation continues to use cached data for proper scroll position restoration
loading.js
files maintain their 5-minute cache duration
Note: If you need to maintain Next.js 14's Client Router Cache behavior, configure the staleTimes option in your next.config.js
file.
const nextConfig = {
experimental: {
staleTimes: {
dynamic: 30,
},
},
};
export default nextConfig;
React 19 Support
React 19 introduces a host of groundbreaking updates. A key highlight is its support for the powerful new features including the React Compiler for optimised performance, actions, use server, and more. Although React 19 is still in beta, Next.js 15 fully supports it, allowing you to leverage these exciting advancements seamlessly within your Next.js projects.
React 19 added in the dependencies: After Upgrading to Nextjs.15
Turbopack Dev
Another change introduced in Next.js 15 is Turbopack. Turbopack is a Vercel bundler optimised for speed, promising faster build times. Turbopack also lets you iterate quickly by reducing the time needed to bundle large codebases. By default, Turbopack is disabled.
To enable it, add --turbo
to the package.json
file:
"scripts": {
"dev": "next dev --turbopack"
}
When you upgrade to Next.js 15 or create a new Next.js project, you can also opt-in to use Turbopack.
Turbopack: After upgrading to Nextjs.15
Note: Turbopack works only in development mode and will not improve site performance in production.
Static Indicator
Next.js 15 added a visual indicator that shows static routes during development. This visual cue helps you debug and optimise performance by making it easier to understand how pages are rendered.
Static Route Indicator
unstable_after
API
This new experimental API addresses a key limitation in serverless functions: the inability to separate primary and secondary processing tasks.
Typically, a server might render data for the client while also performing background tasks like logging or notifying an admin about errors. In traditional setups, serverless functions terminate immediately after sending the response, preventing background processing.
However, since these background tasks don’t need to delay the client’s experience, the after()
API offers an improved solution. It separates tasks into:
- Primary tasks that directly generate the user response
- Secondary tasks like logging, analytics, and external system updates
With unstable_after()
, you can run secondary tasks only after the response has been sent to the client, keeping the application fast and responsive. To enable the after feature, take the following steps:
add experimental.after
tonext.config.js
: ```jsx const nextConfig = { experimental: { after: true, }, };
export default nextConfig;
2. import the function in your desired project–Server Components, Server Actions, Route Handlers, or Middleware.
```jsx
import { unstable_after as after } from 'next/server';
import { log } from '@/app/utils';
- wrap the background task code inside the
after
function
export default function Layout({ children }) {
// Secondary task
after(() => {
log();
});
// Primary task
return <>{children}</>;
}
This code will run only after all components have been rendered, improving the user experience by reducing wait time.
Enhanced Forms (next/form)
The Next.js form component is designed for a specific use case: submitting a form that redirects your users to another page. For example, suppose you have a form on the homepage that navigates to a /search page with the user’s query. With this form component, you can easily redirect and prepopulate search parameters.
The process is simple in Next.js 15, unlike the previous version of Next.js, which required custom code or a full page refresh. The new form component enables client-side navigation, prefetching, and parameter management, making your application faster and more responsive. To use the form component, take the following steps:
- Import the
Form
component from’next/form’
import Form from 'next/form';
- Add the query and target page, and hit submit. Next.js will handle the navigation and populate the search parameters on the new page
Note: The new form component is useful only when navigating from one page to another as a form action.export default function Page() { return ( <Form action="/search"> <input name="query" /> <button type="submit">Submit</button> </Form> ); }
TypeScript support for next.config.ts
Next.js configuration now includes TypeScript support by default. When creating a new project, the configuration automatically provides TypeScript types, making it easier for TypeScript users to get started.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
Self-hosting Improvements
Additionally, this release includes improvements for self-hosting. Next.js 15 offers enhanced control over Cache-Control directives for self-hosted applications. You can now configure the expireTime
value in next.config.js
, replacing the former experimental.swrDelta
option.
This update sets the default value to one year, ensuring most CDNs can effectively apply stale-while-revalidate
behaviour as intended. Next.js also respects custom Cache-Control headers without overriding them with default values, providing full compatibility with various CDN configurations.
In addition, image optimisation has become simpler. Previously, installing sharp
was recommended for optimizing images, though this step was often overlooked. With Next.js 15, sharp
is now automatically used when running next start
or using standalone output mode, eliminating the need for manual installation.
Although self-hosting may still have limitations, these adjustments reflect a step toward greater flexibility for developers.
Server Actions Security
Next.js 15 introduces enhanced security measures for server actions, though the change may be more incremental than transformative. Typically, each server action in Next.js essentially creates a new public API, and you might not realise this exposure, leading to unsecured server actions that could be called unexpectedly.
To mitigate this, Next.js 15 solves this issue in two ways:
Next.js 15 skips deploying server actions that aren’t used in a project, reducing the surface area of potential vulnerabilities.
Next.js introduced unique action IDs assigned to each server action to make them harder to exploit.
These updates aim to enhance security and performance but should be viewed as complementary to existing security best practices.
ESLint 9 Support
Next.js 15 has integrated support for ESLint 9, marking an important transition as ESLint 8 reaches its end-of-life. To ensure a smooth upgrade experience, Next.js includes an automatic migration feature for developers who haven't yet moved to the new configuration format.
When you upgrade to ESLint 9, Next.js will automatically apply the ESLINT_USE_FLAT_CONFIG=false
setting, allowing you to maintain your existing configuration structure while you plan your migration strategy.
As part of this update, Next.js has also streamlined its linting command line interface. Specifically, the --ext
and --ignore-path
flags are no longer supported, reflecting the modernisation of Next.js's linting capabilities.
Conclusion
Next.js 15 introduces amazing features to Next.js-oriented users and developers. This major update introduces new changes, including enhanced caching and Async Request APIs, contributing significantly to the overall improvement of your Next.js project.
For more information on Next.js 15, see Next.js 15 docs and Next.js conf
For more information on Next.js features, see Next.js 15 Breakdown
For additional information on codemod, see Next.js documentation on Upgrading Codemods
Subscribe to my newsletter
Read articles from Abdul-Majid Aladejana directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by