### What?
Promotes two experimental error-handling APIs to stable by dropping the
`unstable_` prefix:
- `unstable_catchError` → `catchError` (exported from `next/error`)
- `unstable_retry` → `retry` (the prop Next.js injects into `error.js` /
`global-error.js` boundaries and `catchError` fallbacks)
### Why?
Both were introduced as `unstable_` in `v16.2.0` and are now stable as
of `v16.3.0`. The experimental prefix should be removed from the stable
surface.
### How?
- Renamed the `next/error` export (`error.d.ts`, `error.js`,
`src/api/error.ts`, `src/api/error.react-server.ts`) and the
implementation in `client/components/catch-error.tsx`.
- Renamed the framework-injected `retry` prop across
`error-boundary.tsx`, `builtin/global-error.tsx`, the dev-overlay error
boundary, and the `ErrorInfo` type.
- Updated the RSC client-only export list in the SWC transform
(`react_server_components.rs`) so importing `catchError` in a Server
Component still produces the "only available in Client Components"
error.
- Updated the thrown error messages and their `errors.json` entries
(codes 1138/1139), the TypeScript-plugin serialization-exemption rule,
all affected tests, the rspack test manifests, and the docs.
- Docs version-history tables keep the historical `unstable_` rows and
add a `v16.3.0` row noting the rename to stable.
This is a clean rename with **no** backward-compatible `unstable_`
alias.
### Verification
- Passed: pre-commit hooks (`prettier`, `eslint --fix`, `rustfmt`) on
all 35 changed files.
- Passed: repo-wide grep confirms no remaining `unstable_catchError` /
`unstable_retry` outside the intended historical version-history rows.
- In progress locally (covered by CI): `pnpm build-all` (native SWC
still compiling on a fresh worktree), then `pnpm --filter=next types`
and the targeted e2e suites (`app-dir/catch-error`, `app-dir/errors`,
`rsc-build-errors`, typescript-plugin `client-boundary`).
<!-- NEXT_JS_LLM_PR -->
While force-runtime does opt you into runtime prefetching today (i.e. it
does force) the intended semantic is shifting to convey that the Segment
itself is designed for and makes sense (i.e. cost / performance
tradeoff) to runtime prefetch. In the future segments that may be
runtime prefetched might not be for various optimization reasons. We
therefore are renaming the option from force-runtime to allow-runtime.
In the future if we change allow-runtime to sometimes not runtime
prefetch we can always ship a new force-runtime as a codemod option that
recovers the current behavior.
This wording also better demonstrates why this is a feature of the
Segment and not say an option on the link like `<Link
prefetch="force-runtime" />`.
### Fixes#94499
### Summary
The `Instrumentation.onRequestError` type currently exposes the error
parameter as `unknown`, but the docs described it as `Error & { digest:
string }` and the example accessed `err.message` directly.
This updates the docs/example to match the current type and narrows the
caught value before reading `message`.
### Tests:
- `git diff --check`
Clarify caching directives non-persistence across deploys.
- [x] reinforce that in serverless deploys, the `use cache` directive
doesn't persist between requests/different instances either
### What?
Adds an `updateTag` example to the `cacheTag` API reference page.
### Why?
The `cacheTag` docs only showed `revalidateTag` for cache invalidation.
For forms and user-triggered mutations (the dominant use case for
`cacheTag`), `updateTag` is the recommended API because it provides
read-your-own-writes semantics. DX agent friction logs showed agents
defaulting to `revalidateTag` and hitting the deprecated single-arg
error before discovering `updateTag` via web search.
### How?
Added a second example block after the existing `revalidateTag` one,
showing `updateTag('my-data')` in a Server Function.
Co-authored-by: Joseph Chamochumbi <joseph.chamochumbi@vercel.com>
## What?
Closes#94153.
Clarifies that `next-env.d.ts` is managed by Next.js and that its
contents may change over time, including between commands such as `next
dev` and `next typegen`.
## Why?
Projects that still track `next-env.d.ts` can see command-order diffs
when the generated route type imports switch between `.next/dev/types`
and `.next/types`. The docs already recommend excluding the file from
version control; this makes the generated-file contract and manual-edit
guidance explicit.
## Testing
- `npx --yes prettier@3.6.2 --check
docs/01-app/03-api-reference/05-config/02-typescript.mdx`
- `git diff --check`
Fixed `search from` → `search for` and `at the of the page` → `at the
top of the page` (confirmed by checking API ref pages — overview tables
go at the top after a short intro and example).
### What?
Adds a `## Behavior` → `### Cache Components` section to the API
reference pages for `usePathname`, `useSelectedLayoutSegment`, and
`useSelectedLayoutSegments`.
### Why?
When `cacheComponents` is enabled, these hooks suspend during
prerendering on routes that have dynamic params not covered by
`generateStaticParams`. This was previously only hinted at with a
one-line note on `usePathname` and undocumented on the layout segment
hooks, even though they share the same behavior.
### How?
- `usePathname`: replaced the terse one-line note with a `## Behavior` →
`### Cache Components` section explaining when it resolves on the server
vs. suspends. Also added a link to the [Preventing flash before
hydration](/docs/app/guides/preventing-flash-before-hydration) guide in
the existing rewrite-mismatch example, and cleaned up a stray em dash.
- `useSelectedLayoutSegment`: added the same `### Cache Components`
section.
- `useSelectedLayoutSegments`: added the same `### Cache Components`
section.
The rewrite/Proxy hydration-mismatch gotcha is intentionally not
duplicated to the layout segment pages — those hooks derive their value
from the routing tree, not the URL pathname, so rewrites don't produce
the same mismatch.
Documents existing behavior only — no API surface changes.
It is possible to call cached functions from the client, under a couple
of conditions.
Rather than introducing this as a pattern now, let's start with a
callout.
Similarly (extending) Server Functions, the client only sees a
reference, no implementation details leak.
Reframes the Draft Mode guide so the headline is "every Next.js cache
layer is bypassed for the request," not "switch to dynamic rendering."
The previous guide led with an `isEnabled` fork in `getData()`, which
implied users have to branch their fetch code to make caching step
aside. They don't.
Changes:
- Intro lists the bypassed layers (`fetch` cache, `'use cache'`,
`unstable_cache`, ISR response cache).
- Step 3 fetches from a single endpoint with no `isEnabled` branch.
- New Step 4 shows a preview banner + disable handler, which is where
`isEnabled` is actually useful.
- The "Cache Components" example uses `isEnabled` for a UI indicator
instead of branching the fetch URL.
- Two-URL fork demoted to a final section for CMSs that genuinely need
it.
Remove the `instantNavigationDevToolsToggle` experimental flag and show
the Navigation Inspector whenever `cacheComponents` is enabled. Also
updates docs and test fixtures that referenced the old flag.
## Summary
- remove the inaccurate claim that a custom server disables Automatic
Static Optimization
- retain the guidance to use a custom server only when the integrated
router cannot meet an app's requirements
## Verification
- `pnpm prettier --with-node-modules --ignore-path .prettierignore
--check docs/01-app/02-guides/custom-server.mdx`
- commit hook: `prettier --write` and `eslint --fix` for the changed MDX
file
## What?
Updates the Docker section in the deploying guide.
- Rewrites the Docker intro sentence to be more concise and adds a link
to Docker's official [React.js
guide](https://docs.docker.com/guides/reactjs) alongside the existing
[Next.js guide](https://docs.docker.com/guides/nextjs).
## Why?
Add reference to new Next.js Docker guide in the deploying docs.
## How?
Documentation-only change. No code changes.
CC: @icyJoseph
### What?
Fix three broken code examples in the Getting Started section.
### Why?
Per the contribution guide, code blocks should contain a minimum working
example that can be copied and pasted. These examples have bugs that
would cause runtime errors or incorrect behavior:
1. `02-project-structure.mdx`: Extra comma in App Icons table —
`apple-icon` row has `.jpg` `.jpeg`, `.png` while all other rows use
space-separated extensions without commas.
2. `03-layouts-and-pages.mdx`: `Post` component has an unused `post`
prop — the component fetches and renders all posts internally via
`getPosts()`, so the prop is never referenced. Removed the unused prop,
renamed to `Posts` to match its actual behavior, and added the missing
`import { getPosts } from '@/lib/posts'`.
3. `04-linking-and-navigating.mdx`: JSX `Layout` function is missing `{
children }` in its parameter list, causing `ReferenceError: children is
not defined` at runtime. The TSX version is correct.
### How?
- Remove stray comma from App Icons table
- Remove unused `post` prop from `Post` component, rename to `Posts`,
add missing import
- Add `{ children }` destructuring to JSX `Layout` function signature
Closes#93272
---------
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
Continuation of: https://github.com/vercel/next.js/pull/92619
Completing the ISR with Cache Components story.
- [x] mention to cacheHandler and cacheHandlers - these work fine
together (aka with cacheComponents activated)
- [x] prefetch behavior - background revalidation
- [x] link to demo app
- [x] update version history in partialFallbacks config
- [x] align with partialFallbacks config flag removal
Closes: https://github.com/vercel/next.js/issues/91951
We don't refer to these as `path params` anymore, rather called them
`dynamic segments`, which causes a drop-off when people search for
"nextjs path params".
While looking at this, I also noticed that we don't really mention what
a segment, people don't know this necessarily.
We'll have to come back to this point later on because, params are just
placeholders, the dynamic qualifier is too broad, dynamic at build and
runtime is what we mean today, but arguably params known via
generateStaticParams, are no longer dynamic params, they are known set
of params.
### What?
Adds a `compile_route` MCP tool that triggers on-demand compilation of a
specific route (app or pages) without issuing an HTTP request, and
returns any compilation issues.
### Why?
Coding agents and benchmarking workflows need a way to warm the module
graph or measure compile time for a route without standing up a live
backend to satisfy the request. The existing path — hitting the URL —
requires the route's runtime dependencies to be available and couples
compile timing to request handling.
### How?
- New tool `mcp/compile_route` registered in
`get-or-create-mcp-server.ts`, backed by a `compileRoute({ page,
clientOnly })` callback plumbed from the Turbopack hot reloader
- Reuses the dev server's existing on-demand entry path (`ensurePage` /
`handleRouteType`), so the call path matches a first navigation.
- Adds a `subscribeToChanges` opt-out on `ensurePage` and threads it
through `handleRouteType` / `handlePagesErrorRoute`. One-shot MCP
compilations skip HMR subscription wiring — without this, each call
would leak a subscription that fires on every subsequent file change for
the life of the dev server.
- Telemetry: registers `mcp/compile_route` in the `McpToolName` union.
- e2e test in
`test/development/mcp-server/mcp-server-compile-route.test.ts`.
<!-- NEXT_JS_LLM_PR -->
Adapters implementing `modifyConfig` may need to know the absolute
project directory to perform path resolution or apply directory-specific
configuration logic. Without this value, adapters had no reliable way to
determine where the application lives on disk.
onBuildComplete already received the directory as a parameter.
This PR cleans up small documentation and error-page issues: correct
TypeScript spelling where it was written as “Typescript”, use the proper
GitHub label in links, fix the MDN URL for `Map.prototype.values` in the
middleware upgrade guide.
No runtime or API behavior changes.
The "AI agent hint" comment on the docs landing page (`docs/index.mdx`)
tells every agent reading the docs on every task to export
`unstable_instant` to fix slow client-side navigations.
That's too aggressive for an experimental API — `unstable_instant` is
`version: draft` and still reshaping (`unstable_prefetch` just split off
in #92754). The instant-navigation guide already covers the API in
measured language; agents working on slow-navigation tasks will find it
there.
<!-- NEXT_JS_LLM_PR -->
When `cacheComponents` is enabled, the App Router preserves state across
navigations by rendering inactive routes inside React `<Activity>`
boundaries.
As a default behavior, this is a huge convenience because it lets you
navigate between routes without resetting or losing ephemeral UI state
(scroll position, expand/collapse, in-progress form edits). Previously
the only way to do this was to explicitly track each state with an
external state manager, or hoist it to a parent component.
It's still the often the case that you should be explicitly tracking all
important UI state, anyway, so that it survives a hard refresh of the
app, or the browser window being accidentally closed. For example, forms
draft states should be persisted to a server-side database or local
storage engine. If you're already doing that, then it doesn't matter so
much whether client state is preserved via `<Activity>` boundaries or
not.
For the long tail of ephemeral state that is not tracked, Next.js's
philosophy is that it's a better UX default to preserve as much
emphemeral state as possible. It's easier to model the cases where you
_do_ want state to be reset on navigation as exceptions, compared to the
other way around.
However, this is a significant change compared to the pre-Cache
Components previous behavior of Next.js, and compared to other web
frameworks, and indeed the browser's own native bfcache (which
implements state restoration for history traversal navigations only, not
push/replace).
There's are also lots of existing codebases that may rely on the current
behavior, and may break subtly under these new semantics. Even if this
new default unlocks better UX patterns, we don't want to force everyone
to migrate all their code all at once.
So, this PR introduces a drop-in mechanism for opting out of state
preservation when navigating to a previously visited route.
The API is exposed as `useRouter().bfcacheId`. It's intended to be
passed to a React `key`:
<form key={useRouter().bfcacheId}>
The id is contextual: read from a layout, you get the layout's id; read
from a page, you get the page's id. It's stable across back/forward
navigations, `router.refresh()`, server actions that call `refresh()`,
and search-param- or hash-only navigations — i.e., any time the
surrounding segment is preserved. It changes when the segment is freshly
created by a push or replace into a different route.
An important detail is that the previous id is restored during a back/
foward navigation. So state preservation will still work if you navigate
via the browser's back button.
Why add this to `useRouter()` instead of giving it its own hook? The
intent is communicate that `bfcacheId` is not considered an idiomatic
pattern — the recommended fix for "I want this state to reset on
navigation" is almost always something else: an explicit reset in a
submit handler, or a key derived from the underlying data (e.g., a draft
id from the server). `useRouter` is the hook where we expose low-level
APIs that are supported but are only recommended for advanced or
exceptional cases. (For example, instead of `router.push()`, you should
almost always use a `<Link>` component instead.)
Removes the unstable prefix from `unstable_io`. Since this was only
shipped in canaries with the unstable prefix we are going to just remove
it without retaining the unstable prefix simultaneously.
Adds `experimental.instantInsights.validationLevel` so an app can opt
into instant validation for all routes by default.
There are two public levels
* `manual-warning`: currently the default. this mode won't enable
instant validation except where you add `export const unstable_instant =
true` into your Layouts, Pages, and Default files
* `warning`: This mode will enable instant validation for all Page and
Default files. You can override this with `export const unstable_instant
= false` in Layouts, Pages, and Default files to tune what navigations
are validated.
In addition this change disables build time validation.
There is a now undocumented way to get back to running build validation
with
* `experimental-error`
* `experimental-manual-error`
The build validation isn't ready for users because we need to overhaul
the setup where you provide cookies and other sample values so we can
run validation without a live session like we do in dev.
To further support this the two remaining object properties that are
also not ready for active use, `samples` and `from` are being updated to
`unstable_samples` and `unstable_from` respectively.
We will continue to iterate on build time validation but may remove
these APIs or alter their semantics as we figure out a more ergonomic
way to support this setup.
In anticipation of multiple levels the object form of `instant` can be
provided a level value `warning` or `experimental-error`. this latter
option is similarly undocumented so documented behavior will only cover
`warning` which is generally a synonym of true unless you are activating
error by default
Remove
/02-pages/03-building-your-application/03-data-fetching/03-forms-and-mutations.mdx
- which is not reachable from the site, because it has been replaced by
the https://nextjs.org/docs/pages/guides/forms guide.
### What?
Clarifies the Proxy docs so the “Why Proxy” section no longer appears to
contradict the Runtime section.
### Why?
The Runtime section states that Proxy defaults to the Node.js runtime,
but the “Why Proxy” section still said Middleware defaults to the Edge
Runtime. Since Middleware was renamed to Proxy in v16, that wording
could make readers think the current default runtime is Edge.
### How?
Updates the “Why Proxy” paragraph to frame Edge Runtime wording as
historical context rather than current default behavior.
Also updates the v16.0.0 version history entry to mention that Proxy
defaults to the Node.js runtime.
Closes#93463
---------
Co-authored-by: Joseph <sephxd1234@gmail.com>
### What?
Adds an `other` field to each rule in `MetadataRoute.Robots` so
`app/robots.ts` can emit non-standard per-user-agent directives (e.g.
Seznam `Request-Rate`, Yandex `Clean-param`) alongside the standard
`allow`/`disallow`/`crawlDelay` fields.
```ts
// app/robots.ts
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{ userAgent: '*', allow: '/' },
{
userAgent: 'SeznamBot',
allow: '/',
other: { 'Request-Rate': '10/1m' },
},
],
}
}
```
emits:
```
User-Agent: *
Allow: /
User-Agent: SeznamBot
Allow: /
Request-Rate: 10/1m
```
### Why?
The current `resolveRobots` serializer only writes the four standard
rule fields and silently drops everything else, so users who need
non-standard directives (common for Seznam, Yandex, and other crawlers)
have to delete `robots.ts` entirely and hand-maintain a static
`robots.txt`, losing the type-safe, code-driven workflow.
### How?
- **Type**
(`packages/next/src/lib/metadata/types/metadata-interface.ts`): factored
a shared `RobotsRuleBase` containing the existing optional fields plus
`other?: Record<string, string | number | Array<string | number>>`.
`RobotsFile.rules` still enforces `userAgent` as required on the array
form.
- **Serializer**
(`packages/next/src/build/webpack/loaders/metadata/resolve-route-data.ts`):
after `Crawl-delay` and before the trailing blank line, walks
`rule.other` and emits `Key: value` lines preserving casing. Array
values expand into repeated lines; `null`/`undefined` entries are
skipped. Keeps non-standard directives scoped to their `User-Agent`
block.
- **Tests**: added two `resolveRobots` cases covering the `Request-Rate`
reproduction and an array-valued `Clean-param`, plus null/undefined
handling. Existing snapshots are unchanged, proving the change is
additive.
- **Docs**: new "Non-standard directives" section in
`docs/01-app/03-api-reference/03-file-conventions/01-metadata/robots.mdx`
with TS/JS examples and updated `Robots` type reference.
Fixes#89521
<!-- NEXT_JS_LLM_PR -->
Added an important exception (for when client components wrap server
components) under an original line that seems to claim a universal law.
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:
## For Contributors
### Improving Documentation
- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide
### Fixing a bug
- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
### Adding a feature
- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
### Signed commits
- This repository requires verified commit signatures on protected
branches.
- If this pull request is blocked for unsigned commits, re-sign the
commits and force-push the branch.
- A `Signed-off-by` line in the commit message is not enough.
## For Maintainers
- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change
### What?
### Why?
### How?
Closes NEXT-
Fixes #
-->
---------
Co-authored-by: Joseph <sephxd1234@gmail.com>
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
Note: re-apply of #92817
### What?
Deprecates the Edge Runtime and the `preferredRegion` route segment
config that only applied to edge runtime with build-time warnings,
TypeScript IDE hints, error documentation pages, and documentation
updates.
### Why?
Edge Runtime in Next.js is being deprecated in favor of the Node.js
runtime (the default). The `preferredRegion` route segment config is
tightly coupled to Edge Runtime and is also being deprecated.
### How?
**Edge Runtime deprecation:**
- Added `warnAboutEdgeRuntime()` helper using `Log.warnOnce` in
`packages/next/src/build/warn-about-edge-runtime.ts`
- Warning fires in `get-page-static-info.ts` (Webpack dev/build +
Turbopack build) and `turbopack-utils.ts` (Turbopack dev) when `runtime
= 'edge'` is detected
- Marked `'edge'` as `@deprecated` in the TypeScript language service
plugin (`rules/config.ts`)
- Created `errors/edge-runtime-deprecated.mdx` with migration guidance
- Updated documentation across 11 files to add deprecation notices or
simplify/remove outdated Edge Runtime references
**`preferredRegion` deprecation:**
- Added `warnAboutPreferredRegion()` helper in the same file
- Warning fires in `get-page-static-info.ts` for both App Router
(`config.preferredRegion`) and Pages Router (`config.config?.regions`)
- Marked `preferredRegion` as `@deprecated` in the TypeScript language
service plugin and the generated types (`next-types-plugin`)
- Created `errors/preferred-region-deprecated.mdx` with migration
guidance (remove the export)
- Updated documentation across 4 files to add deprecation notices
**Tests:**
- Added `test/e2e/edge-runtime-deprecated/` with fixture and assertions
for the edge runtime warning (dev + production)
- Added `test/production/preferred-region-deprecated/` with fixture and
assertions for the preferredRegion warning (production only -- Turbopack
dev does not surface `preferredRegion` from the Rust layer to JS)
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
This flag has been enabled by default on canary for some time. This
turns it on by default to ensure that CDNs that don't respect `Vary`
properly distinguish between different response types.