Tuesday, December 10th 2024
Next.js 15.1
Posted byNext.js 15.1 brings core upgrades, new APIs, and improvements to the developer experience. Key updates include:
- React 19 (stable): Support for React 19 is officially available in both Pages Router & App Router.
- Improved Error Debugging: Enhanced DX and better source maps for the browser and the terminal.
after
(stable): New API to execute code after a response has finished streaming.forbidden
/unauthorized
(experimental): New APIs to enable more granular authentication error handling.
Upgrade today, or get started with:
# Use the automated upgrade CLI
npx @next/codemod@canary upgrade latest
# ...or upgrade manually
npm install next@latest react@latest react-dom@latest
# ...or start a new project
npx create-next-app@latest
React 19 (stable)
Next.js 15.1 now fully supports React 19:
- For the Pages Router: you can now use React 19 stable without needing the Release Candidate or Canary releases, alongside continued support for React 18.
- For the App Router: we will continue to provide React Canary releases built-in. These include all the stable React 19 changes, as well as newer features being validated in frameworks, prior to a new React release.
Since the Next.js 15 release, a significant addition to React 19 was “sibling pre-warming”.
For a comprehensive overview of React 19’s updates, please refer to the official React 19 blog post.
Improved Error Debugging
We’ve made improvements to error debugging in Next.js, ensuring you can quickly locate the source of issues, whether they appear in the terminal, browser, or attached debuggers. These enhancements apply to both Webpack and Turbopack (now stable with Next.js 15).
Source Maps Enhancements
Errors are now easier to trace back to their origin through the improved use of source maps. We’ve implemented the ignoreList
property of source maps, which allows Next.js to hide stack frames for external dependencies, making your application code the primary focus.
For slightly more accurate source mapping of method names, we suggest adopting Turbopack (now stable), which has improved handling and detection of source maps over Webpack.
For library authors: We recommend populating the
ignoreList
property in sourcemaps when publishing your libraries, especially if they are configured as external (e.g. in theserverExternalPackages
config).
Collapsed Stack Frames
We’ve improved the logic for collapsing stack frames to highlight the most relevant parts of your code.
- In the browser and error overlay: Stack frames from third-party dependencies are hidden by default, focusing on your application code. You can reveal the hidden frames by clicking “Show ignored frames” in the devtools or the overlay.
- In the terminal: Third-party dependency frames are also collapsed by default, and error formatting now aligns with the browser output for a consistent debugging experience. Errors are replayed in the browser to ensure you don’t miss important information during development if you need the entire stack trace.
Enhanced Profiling
Ignored stack frames are also recognized by built-in browser profilers. This makes profiling your application easier, allowing you to pinpoint slow functions in your code without noise from external libraries.
Improved with the Edge Runtime
When using the Edge runtime, errors are now displayed consistently across development environments, ensuring seamless debugging. Previously, logged errors would only include the message and not the stack.
Before and after
Terminal Before:
⨯ app/page.tsx (6:11) @ eval
⨯ Error: boom
at eval (./app/page.tsx:12:15)
at Page (./app/page.tsx:11:74)
at AsyncLocalStorage.run (node:async_hooks:346:14)
at stringify (<anonymous>)
at AsyncLocalStorage.run (node:async_hooks:346:14)
at AsyncResource.runInAsyncScope (node:async_hooks:206:9)
digest: "380744807"
4 | export default function Page() {
5 | const throwError = myCallback(() => {
> 6 | throw new Error('boom')
| ^
7 | }, [])
8 |
9 | throwError()
GET / 500 in 2354ms
Terminal After:
⨯ Error: boom
at eval (app/page.tsx:6:10)
at Page (app/page.tsx:5:32)
4 | export default function Page() {
5 | const throwError = myCallback(() => {
> 6 | throw new Error('boom')
| ^
7 | }, [])
8 |
9 | throwError() {
digest: '225828171'
}
Error Overlay Before
Error Overlay After
These improvements make errors clearer and more intuitive, allowing you to focus your time building your application rather than debugging.
We’re also thrilled to announce the introduction of a redesigned UI for the error overlay, coming in upcoming releases.
after
(stable)
The after()
API is now stable following its introduction in the first Next.js 15 RC.
after()
provides a way to perform tasks such as logging, analytics, and other system synchronization after the response has finished streaming to the user, without blocking the primary response.
Key changes
Since its introduction, we’ve stabilized after()
and addressed feedback including:
- Improved support for self-hosted Next.js servers.
- Bug fixes for scenarios where
after()
interacted with other Next.js features. - Enhanced extensibility, enabling other platforms to inject their own
waitUntil()
primitives to powerafter()
. - Support for runtime APIs such as
cookies()
andheaders()
in Server Actions and Route Handlers.
import { after } from 'next/server';
import { log } from '@/app/utils';
export default function Layout({ children }) {
// Secondary task
after(() => {
log();
});
// Primary task
return <>{children}</>;
}
Read more about the after
API and how to leverage it in the documentation.
forbidden
and unauthorized
(experimental)
Next.js 15.1 includes two experimental APIs, forbidden()
and unauthorized()
, based on community feedback.
We’d love your feedback — please try it in your development environments and share your thoughts in this discussion thread.
Overview
If you’re familiar with the App Router, you’ve likely used notFound()
to trigger 404 behavior alongside the customizable not-found.tsx
file. With version 15.1, we’re extending this approach to authorization errors:
• forbidden()
triggers a 403 error with customizable UI via forbidden.tsx
.
• unauthorized()
triggers a 401 error with customizable UI via unauthorized.tsx
.
Good to know: As with
notFound()
errors, the status code will be200
if the error is triggered after initial response headers have been sent. Learn more.
Enabling the feature
As this feature is still experimental, you’ll need to enable it in your next.config.ts
file:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
experimental: {
authInterrupts: true,
},
};
export default nextConfig;
Note:
next.config.ts
support was introduced in Next.js 15. Learn more.
Using forbidden()
and unauthorized()
You can use forbidden()
and unauthorized()
in Server Actions, Server Components, Client Components, or Route Handlers. Here’s an example:
import { verifySession } from '@/app/lib/dal';
import { forbidden } from 'next/navigation';
export default async function AdminPage() {
const session = await verifySession();
// Check if the user has the 'admin' role
if (session.role !== 'admin') {
forbidden();
}
// Render the admin page for authorized users
return <h1>Admin Page</h1>;
}
Creating custom error pages
To customize the error pages, create the following files:
import Link from 'next/link';
export default function Forbidden() {
return (
<div>
<h2>Forbidden</h2>
<p>You are not authorized to access this resource.</p>
<Link href="/">Return Home</Link>
</div>
);
}
import Link from 'next/link';
export default function Unauthorized() {
return (
<div>
<h2>Unauthorized</h2>
<p>Please log in to access this page.</p>
<Link href="/login">Go to Login</Link>
</div>
);
}
We'd like to thank Clerk for proposing this feature through a PR and assisting us in prototyping the API. Before we stabilize this feature in 15.2, we're planning on adding more capabilities and improvements to the APIs to support a wider range of use cases.
Read the documentation for the unauthorized
and forbidden
APIs for more details.
Other Changes
- [Feature] Use ESLint 9 in
create-next-app
(PR) - [Feature] Increase max cache tags to 128 (PR)
- [Feature] Add an option to disable experimental CssChunkingPlugin (PR)
- [Feature] Add experimental CSS inlining support (PR)
- [Improvement] Silence Sass
legacy-js-api
warning (PR) - [Improvement] Fix unhandled rejection when using rewrites (PR)
- [Improvement] Ensure parent process exits when webpack worker fails (PR)
- [Improvement] Fixed route interception on a catch-all route (PR)
- [Improvement] Fixed response cloning issue in request deduping (PR)
- [Improvement] Fixed Server Action redirects between multiple root layouts (PR)
- [Improvement] Support providing MDX plugins as strings for Turbopack compatibility (PR)
Contributors
Next.js is the result of the combined work of over 3,000 individual developers. This release was brought to you by:
- The Next.js team: Andrew, Hendrik, Janka, Jiachi, Jimmy, Jiwon, JJ, Josh, Jude, Sam, Sebastian, Sebbie, Wyatt, and Zack.
- The Turbopack team: Alex, Benjamin, Donny, Maia, Niklas, Tim, Tobias, and Will.
- The Next.js Docs team: Delba, Rich, Ismael, and Lee.
Huge thanks to @sokra, @molebox, @delbaoliveira, @eps1lon, @wbinnssmith, @JamBalaya56562, @hyungjikim, @adrian-faustino, @mottox2, @lubieowoce, @bgw, @mknichel, @wyattjoh, @huozhi, @kdy1, @mischnic, @ijjk, @icyJoseph, @acdlite, @unstubbable, @gaojude, @devjiwonchoi, @cena-ko, @lforst, @devpla, @samcx, @styfle, @ztanner, @Marukome0743, @timneutkens, @JeremieDoctrine, @ductnn, @karlhorky, @reynaldichernando, @chogyejin, @y-yagi, @philparzer, @alfawal, @Rhynden, @arlyon, @MJez29, @Goodosky, @themattmayfield, @tobySolutions, @kevinmitch14, @leerob, @emmanuelgautier, @mrhrifat, @lid0a, @boar-is, @nisabmohd, @PapatMayuri, @ovogmap, @Reflex2468, @LioRael, @betterthanhajin, @HerringtonDarkholme, @bpb54321, @ahmoin, @Kikobeats, @abdelrahmanAbouelkheir, @lumirlumir, @yeeed711, @petter, and @suu3 for helping!