Everything You Need To Know About Next.js 15
What's new with Next.js 15: Async Request APIs, Improved Caching, Enhanced Hydration Errors, Form Component, Support for React 19, and more!
Kaushal Joshi
Dec 03, 2024 • 10 min read
Every time Vercel announces anything new, it becomes an instant headline! Last week, Vercel announced Next.js 15 just before the Next.js Conf, and, as always, it’s buzzing in the web dev community.
In this blog, let's dive into what Next.js 15 offers, look at breaking changes, and some other improvements over its last version, and explore how to upgrade to the latest version.
Next.js 15 — In a Nutshell
Next.js 15 focuses heavily on stability while packing some exciting updates.
Key highlights:
A CLI tool for seamless upgrades to the latest Next.js and React versions
Request APIs now support asynchronous handling
fetch
requests,GET
Route handlers, and client navigations aren’t cached by default!Hydration error messages are more helpful
Support for React 19 and React Compiler
A new
<Form />
component for handling HTML forms with client-side navigationServer Actions are now more secure
Let’s break down each of these updates.
Codemod CLI
Each Next.js release includes Codemods to simplify upgrading. With Next.js 15, the Codemod CLI has become even more efficient, making it easier to transform code and manage dependencies.
This CLI tool helps you upgrade your codebase to the latest stable versions of Next.js. It will automatically update your dependencies, show available codemods, and guide you through applying them.
In your existing Next.js project, execute the following command:
npx @next/codemod@canary upgrade latest
The canary
tag uses the latest version of the codemod while the latest specifies the Next.js version. The Next.js team recommends using the Canary version even if you are upgrading to the latest Next.js version.
Breaking Change: Async Request APIs
Previously in Next.js, request-specific data like headers, cookies, params, and search parameters was synchronous. That means the rendering process was on halt until the requested data was sent by the server. However, only a few components from the page depend on request-specific data. Hence it’s unnecessary to wait for the requested data to render them.
To optimize this, APIs that rely on request-specific data are now asynchronous. This change affects the following APIs:
cookies
headers
draftMode
params
inlayout.js
,page.js
,route.js
,default.js
,generateMetadata
, andgenerateViewport
searchParams
inpage.js
For example, here’s how you’d use headers in Next.js 14 and Next.js 15:
import { headers } from "next/headers";
// Next.js 14
const headersList = headers();
const userAgent = headersList.get("user-agent");
// Next.js 15
const headersList = await headers();
const userAgent = headersList.get("user-agent");
How to migrate request-specific APIs to be asynchronous in Next.js 15?
The Codemod allows you to migrate request-specific APIs like cookies and headers with a single command. Inside your Next.js project, execute the following command:
npx @next/codemod@canary next-async-request-api .
You can still use request-specific APIs synchronously like you used before. But Next.js will show a warning in both production and development. From Next.js 16, you must use asynchronous APIs to access such data.
Breaking Change: Caching is (finally) Improved!
Next.js introduced an aggressive caching strategy in Next.js 14. Many routes and HTTP requests were cached by default. You, as a developer must need to manually opt out of caching if you want to.
Everybody hated this update. This made caching more confusing and difficult to work with. The community backfired and provided harsh feedback on this. A common feedback was that caching should always be ‘opt-in’ by default, not ‘opt-out’.
The Next.js team heard its community and made significant improvements in how Next.js caches data. With that latest update, caching is now opt-in
behavior instead of opt-out
. Simply put, no routes are being cached by default by Next.js! You, as a developer, must opt-in manually to cache the route.
fetch
requests and GET
Route Handlers are no longer cached by default
This was one of the critical updates that became a point of harsh criticism among developers.
In Next.js 14, force-cache
was the default option, which was used to cache requests. With Next.js 15, no-store
is the default option. So you must manually add cache: { “force-cache” }
in your fetch
call to cache the request.
fetch(api, { cache: "force-cache" | "no-store" });
Similarly, GET
HTTP methods were also cached by default in Next.js 14. With the latest Next.js 15, no GET
method is cached! You can opt into caching by using the static route config position such as the following:
export dynamic = "force-static";
One thing to note here is that special Route Handlers like sitemap.ts
, opengraph-iamge.tsx
, and other metadata files remain static by default. They become dynamic only if you use a dynamic function or a dynamic config option within it.
Page components are not cached by the Client Router Cache
Next.js 14.2.0 introduced an experimental staleTimes
flag to allow custom configuration of the Router cache. With Next.js 15, the default behavior for Page segments is 0 seconds. Basically, the client will always reflect the latest data from the Page that becomes active when a user is navigating through the app.
Some important configurations remain unchanged even with the latest update. This includes:
Shared layout data: It won’t be prefetched from the server, and would support partial rendering.
Back/forward navigation: Navigating back and forth in your app will restore necessary information from the cache to ensure the browser can restore the scroll position.
loading.js
: The loading file will remain cached for 5 minutes unless you’ve set a value instaleTimes.static
manually.
If you want to opt into the previous Client Router Caching behavior, add the following configuration in your Next config file:
const nextConfig = {
experimental: {
staleTimes: {
dynamic: 30,
},
},
};
export default nextConfig;
With these adjustments, developers now have full control over caching, enabling fine-tuned performance management, especially when dealing with fast-changing content like news feeds, live stats, or real-time chat applications.
Enhanced Hydration Error Messages
Hydration errors, which occur when the server-rendered markup doesn’t match the client-side render, have been a pain point in previous versions of Next.js. These errors could be challenging to debug, often requiring developers to sift through large codebases to pinpoint the cause.
In Next.js 15, hydration error messages have been greatly improved. Not only do they provide more detailed information, but they also offer code context and suggested solutions. Hydration errors now display the source code of the error with suggestions on how to address the issue.
Let’s see an example of how hydration error was shown in Next.js 14.1:
In Next.js 15, this is how hydration errors are shown:
This is a huge productivity boost, especially for large applications, as it significantly reduces debugging time and provides developers with the guidance needed to make necessary corrections quickly.
The New <Form />
Component
Next.js 15 introduces a form component that is extremely useful for a very niche use case that we only might use once throughout our application. The new <Form />
component extends the native HTML <form>
element with prefetching, client-side navigation, and progressive enhancements.
The niche use case I mentioned in the previous paragraph is when you want to render a new page based on the input user provided in the form. The best example of this is a search form. A user enters a search query in the form, and the page shows relevant data. Let’s look at an example:
import Form from "next/form";
export default function Page() {
return (
<Form action="/search">
<input name="query" />
<button type="submit">Submit</button>
</Form>
);
}
This form navigates to /search
by passing the URL path in the action
attribute. The form data will be encoded into the URL as search parameters. So, the URL would look like this:
/search?query=peerlist
On the /search
page, here’s how you can access the query
search parameter:
import { getSearchResults } from "@/lib/search";
import { BasicPageType } from "@/types";
export default async function SearchPage({ searchParams }: BasicPageType) {
const results = await getSearchResults(searchParams.query);
return <div>...</div>;
}
Here’s the interesting part: When the <Form />
component is visible on the browser’s viewport, Next.js will prefetch the shared UI on the /search
page. When the user submits the form, the app will instantly navigate to the /search
route and show the loading UI while dynamic data is being fetched.
Next.js 15 Supports React 19
With the release of Next.js 15, the framework is aligning closely with the anticipated capabilities of React 19. This alignment allows developers to benefit from the latest features in React, particularly for applications using the React Server Components, which now runs on React 19’s release candidate (RC) version.
While React 19 is still in RC, extensive testing across various applications and close collaboration between the Next.js and React teams ensures that it's stable and robust for production.
Next.js 15 uses React 19 by default. It also also offers backward compatibility with React 18. If you want to use React 18, you can use the traditional Pages Router, whereas if you want to use the latest React 19 RC, you can continue using the App Router.
Experimental: React Compiler
React.js introduced a new React compiler along with React 19. The compiler reduces the amount of manual memorization developers have to go through hooks like useMemo
and useCallback
. It automatically figures out parts of code that need to be memorized and makes your code less error-prone.
Next.js 15 supports React Compiler as an experimental feature. Currently, it is only available as a Babel plugin, which could slow down your development and build times.
Server Actions are now more secure
Server Actions are publically accessible HTTP endpoints. Even though Next.js claims that this behavior is technically correct, the team was aware that it can lead to unauthorized access.
Hence, Next.js 15 introduces two enhancements:
Dead code elimination: Unused Server Actions won't have their IDs exposed to the client-side JavaScript bundle, reducing bundle size and improving performance.
Secure action IDs: Next.js now creates unguessable, non-deterministic IDs to allow the client to reference and call the Server Action. These IDs are periodically recalculated between builds for enhanced security.
Other Important Changes in Next.js 15
There is much more to Next.js 15 than what we covered. Here’
Support for ESLint 9: As ESLint 8 reached its end of life this month, Next.js 15 now fully supports ESLint 9. However, you can still choose whether you want to use ESLint 8 or 9 in your app.
TypeScript support for config file: The Next config file is now typesafe. You can import
NextConfig
type fromnext
and apply it to the app’s config object.Static Route Indicator: Next.js 15 displays a Static Route Indicator during development mode to help you identify which routes are static or dynamic.
Turbopack Dev is stable: Turbopack is stable and ready to be used in Next.js 15. You can use
next dev --turbo
to use Turbopack in your existing Next.js apps.unstable_after
(Experimental): This feature allows you to execute code once all the information is sent to the client. This is particularly useful when you want to log something to a server, send an email to admins, or perform some tasks that a client doesn’t need to wait for.
Wrapping Up
Next.js 15 brings so many amazing features that’ll surely make working with Next.js joyful again! It’s exciting when a company listens to community feedback and ships what they asked for.
Have you tried Next.js 15 yet? How was your experience with it? What features did you like the most? Or, what did you not like about it? I’d love to know. I am most active on Peerlist and Twitter if you want to say hi.
If you are looking for Next.js jobs, or want to share a side project you built with Next.js, Check out Peerlist. It's a professional network to show & tell what you are working on!
Until then, happy coding! 🧑💻