Logo

Next.js 15 is HERE! 10 must-have features you can't miss

Written by
Blog by Stephan Moerman
Stephan Moerman
Published on
Views
Reading time
13 min read
Next.js 15 is HERE! 10 must-have features you can't miss

A long-awaited update has landed! OnOctober 21st 2024, Vercel finally released Next.js 15 stable, and it's ready for production. I've been testing the earlier versions (canary and RC) in non-production environments over the past few months, and have compiled a list of the best changes and new features that have been introduced in Next.js 15. In this article I'm going to cover the 10 most important new features that you need to know about.

Effortless upgrades (new automated CLI)

The first and in my opinion most important upgrade is the newly introduced next CLI that allows you to update next.js applications or the React version inside them to the latest version.

So how do you use the new next CLI? It's actually pretty easy, so lets take a look at the code snippet that Vercel posted in their article.

Terminal
# Use the new automated upgrade CLI
npx @next/codemod@canary upgrade latest
 
# ...or upgrade manually
npm install next@latest react@rc react-dom@rc

The way you use the CLI is to define the codemod, in the above example canary is the version of the codemod, and the latest is the version of Next.js that you want to upgrade to. You could change this to a specfic version, or just use the latest stable version.

In case you've never heard of Codemods, here's a brief explanation. Codemods are scripts that automate the process of modifying your codebase. They enable you to make extensive changes across many files without needing to edit each one manually.

Next.js offers codemod tools to simplify upgrading your codebase whenever there's an update or deprecation of an API.

If you want to learn more about the Next.js CLI, you can check out the official documentation.

Async requests: next-level performance! (breaking change)

Traditionally, server-side rendering (SSR) requires the server to wait for the request object before starting to render. However, if the rendering is not dependent on headers or cookies, this process has been modified to begin without waiting. As a result, headers, cookies, params, and searchParams are now handled asynchronously.

Here's an example of what this looks like:

page.tsx
import { cookies } from "next/headers";
 
export async function AdminPanel() {
  const cookieStore = await cookies();
  const token = cookieStore.get("token");
 
  // ...
}

Previously you did not have to use await, but since it's now Promise based, this has changed.

Since this is a breaking change, you have multiple ways of upgrading your code to this new async request API spec. One is to use the codemod command:

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

Alternatively you can go through the upgrade guide and manually make changes as you work through your upgrade to Next.js 15, which is likely the approach that most developers will want to take as there are still quite a few cases where the codemod can't fully migrate your code.

Async requests: before and after

In case you are pretty new to Next.js, let's take a look at a quick before and after.

before-after/page.tsx
// Before
type Params = { slug: string }
 
export function generateMetadata({ params }: { params: Params }) {
  const { slug } = params
}
 
export default async function Layout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Params
}) {
  const { slug } = params
}
 
// After
type Params = Promise<{ slug: string }>
 
export async function generateMetadata({ params }: { params: Params }) {
  const { slug } = await params
}
 
export default async function Layout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Params
}) {
  const { slug } = await params
}

In the old example, we could use params inside the page function, even though it's an async function, and using await was not necessary. It's a minor change, but it's something to keep in mind when you start building a project using the latest version of Next.js 15.

And as always, if you need any help upgrading your Next.js project, feel free to reach out!

Caching strategy in Next.js 15 (breaking change explained)

If you've migrated to App Router or know others who have, you've likely heard complaints about how Next.js 14 handled caching. The documentation was unclear, making it difficult to determine what was or wasn't being cached, and the default caching strategy was overly opinionated.

In Next.js 14, when handling a request (like a GET request) without specified cache options, it defaulted to a force-cache strategy—fetching data from the cache first and updating it from the server if needed. Now, the default has changed to no-store, meaning it always fetches data directly from the server unless otherwise specified.

Every Next.js developer that worked with Next.js 14 experienced the pain of opinionated caching
Every Next.js developer that worked with Next.js 14 experienced the pain of opinionated caching

But finally, this default force-cache strategy is no longer a thing! đź‘Ź

Instead in Next.js 15, no-store is used by default if a cache option is not provided. This means fetch requests will not be cached by default, a VERY much welcomed change.

Caching behavior in Next.js 15

During today's Next.js conf they announced the new use cache feature which comes from the unstable_cache feature, so I'll be writing a new blog post covering the latest Next.js 15 announcements in the coming days. However, this does not mean that you can't use the "old" Next.js 14 caching behavior anymore. You now have three options:

  • Individual fetch calls: setting the cache option to force-cache in a single fetch call, which is the behavior Next.js 14 had as its default.
  • Route leveL: setting the dynamic route config option to force-static for a single route.
  • Layout level: setting the fetchCache route config option to default-cache to override all fetch requests in a Layout or Page to use force-cache unless they explicitly specify their own cache option.

React 19 (RC) is here: backward compatiblity explained

The Next.js team is confident enough in rolling out React 19 RC in Next.js 15, but from my personal experience a lot of dependencies are still throwing errors when upgrading from Next.js 14 to 15. They have adopted it as they mentioned in their article that they have closely worked with the React team and found no issues, and all React 19 APIs are working well. I am personally hoping that in the coming Next.js 15 updates, we will get the stable version of React 19.

The good thing about this update however is that there should be a lot of backward compatibiliy with React 18, so Next.js will still support that and you can continue to use the pages router. They do not however that while it is possible to run the Pages Router on React 18 and the APp Router on React 19 in the same application, the Next.js team does not recommend this setup and honestly I wouldn't recommend this either. You could see a lot of unpredictable behavior and typings inconsistencies, as the underlying APIs and rendering logic between the two versions may not fully align.

When you are upgrading, I have an important tip for you:

If you see a peer dependencies warning, you may need to update react and react-dom to the suggested versions, or you use the --force or --legacy-peer-deps flag to ignore the warning. This won't be necessary once both Next.js 15 and React 19 are stable.

React compiler: write less code & achieve more (experimental)

This is very much in the experimental stage right now as explained by the React team at Meta. What this allows us to do is to write less code and maintain less code by removing all the optimizations that we as React developers would deploy such as useMemo or useCallback.

The React compiler understands your code and is able to add automatic optimizations to your code, reducing the amount of manual meorization that developers have to do through APIs, making code simpler, easier to maintain, and less error prone.

Note: The React Compiler is currently only available as a Babel plugin, which will result in slower development and build times.

Hydration error improvents (better debugging)

Before Next.js 15, hydration errors could get very ambigious really quickly meaning you would sometimes have to spend hours debugging your code to try and find the source of the hydration error. In Next.js 14 the Next team started making improvements to it's error reporting for the development server, but there was still lots to improve.

By default a hydration error happens when there is a difference beetween what the server and client rendered. Previously, it would just show a generic error which would kind of make sense if you know what you're doing, but still wasn't always very clear, such as the one in the image:

Hydration error message in Next.js 14.1
Hydration error message in Next.js 14.1

Next.js 15 has improved this to:

Hydration error message improved in Next.js 15
Hydration error message improved in Next.js 15

So in Next 15 you get much clearer erorrs so you know what to change, for example when you use Date.now() the results will differ on the server and client, and you'll know what parts of your code to change to avoid hydration errors.

Turbopack is FINALLY stable (prepare for takeoff!)

We know that Turbopack has been talked about quite a lot and how it improves the overall performance, reduces the bundle size, etc. If you've tried a cold start with Webpack vs Turbopack you will have noticed this difference.

The Vercel team claims that Turbopack on the vercel.com website, which is a large Next.js app, has seen the following improvements:

  • Up to 76.7% faster local server startup.
  • Up to 96.3% faster code updates with Fast Refresh.
  • Up to 45.8% faster initial route compile without caching (Turbopack does not have disk caching yet, but this is coming soon).

It may not affect really small applications as much, but for Enterprise Next.js applications, this can save you a lot of time when running deployments or doing local development work.

A new secret weapon: unstable_after (experimental)

This is a feature I personally really like, and it's still being worked on, but when you get a chance give it a try in a local development environment: unstable_after.

Imagine that you are actually getting a lot of requests on your server, and you want to log them or send some information to Google Analytics or other third party services such as Sentry. In that particular situation does the user have to wait for you to do all of that and wait to get a response back? Not really. Instead you'd want the user to get the response or rendered HTML sent back as soon as possible, and these are the things (logging, analytics, etc.) that can be done afterwards as well.

When using unstable_after, you can schedule this logging, error reporting or analytical task to be completed after the server sends back the rendered HTML to the client. Here's what this would look like.

To use it, add experimental. after to your next.config.js:

next.config.js
const nextConfig = {
  experimental: {
    after: true,
  },
};
 
export default nextConfig;

Then, you can import the function in Server Components, Server Actions, Route Handlers, or *Middleware.

server-component.tsx
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}</>;
}

You can learn more about unstable_after here: https://nextjs.org/docs/canary/app/api-reference/functions/unstable_after

ESLint 9 support (stay ahead of the curve)

With ESLint 8 being end-of-life (EOL) on October 5, 2024 it was time for the Next.js team to migrate to ESLint 9. I really like this because now our IDEs can work with the latest and greatest ESLint and have a better time debugging our applications before hitting staging or any other development stage.

Note: to ensure a smooth transition, Next.js remain backwards compatible, meaning you can continue using either ESLint 8 or 9.

Additionally, deprecated options like —ext and —ignore-path will be removed when running next lint. Please note that ESLint will eventually disallow these older configurations in ESLint 10, so I recommend starting your migration soon.

Overall, keeping things up-to-date is a thing the Vercel team is doing really well.

Development and build improvements (you'll thank me later)

Next.js 15 brings a lot of improvements in the development and build processes, and there's quite a few to go through.

Server Components HRM and cache

One of the first ones that you'll want to look at is the Server Components HMR. So previously if you saved a file which had a server component it would regenerate the HTML but also make the API calls as well. Now imagine if you're using a 3rd party API such as OpenAI or any other API calls where you have a limited quota per month, you don't really want to waste all of that while developing your application. So with this change, any fetch mode call that is made while working in development mode is now being cached.

Faster static generation for the App Router

If you've worked with Next.js for a while, you know that App Router was introduced in Next 13 and has been the recommended way to go forward. But when generating static content, what the compiler would do is run the rendering twice. Once to get the client data for navigation, and secondly for the initial page with it and this has been reduced to a single call instead. Especially if you have a fetch call in the particular route, and you have not specifically set to use no-store, then it would also cache that as well, which is a nice change.

So these were my 10 favorite updates for Next.js 15, but there is a lot more you can read about on the Next.js blog here: https://nextjs.org/blog/next-15

I'd love to hear from you so send me a message on X and let me know what your favorite changes are!

As always, happy coding! In the coming days I'll post my Next.js 15 coverage blog, so stay tuned for more.

Need help migrating to Next.js 15?

Upgrading to Next.js 15 is packed with amazing new features, but getting everything set up just right can be a bit tricky. From handling breaking changes to optimizing your build process, there's a lot to consider. If you're looking for a smooth, stress-free upgrade, I'm here to help!

Whether you need guidance on specific issues or a complete migration plan, I've got the experience to make your transition to Next.js 15 easy and efficient. Reach out, and let's get your project running on the latest and greatest version.

Contact me today, and let's make your upgrade effortless!