## Summary
**What?** Fixes the error reported for a module that has a top-level
`"use client"` directive and an inline `"use server"` function
directive, e.g. `<form action={async () => { "use server" }} />` in a
client page.
**Why?** Under Turbopack this reported a bogus error — `The "use client"
directive must be placed before other expressions` — even though the
directive was already at the top of the file. The actual mistake (an
inline server action inside a Client Component) was never surfaced.
**How?** On Turbopack's RSC layer the server actions transform runs
*before* the React Server Components assert. When it hoists an inline
action it prepended its generated imports (`registerServerReference`,
action encryption, cache runtime) at index 0 of the module — *above* the
`"use client"` directive, which the transform does not consume. The RSC
assert then saw an import before the directive and reported it as
misplaced.
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
There have been breaking changes to the AST:
- `BlockStmtOrExpr` is now `ArrowFunctionBody`
- function bodies are `FunctionBody` instead of `BlockStmt`
- JSX text literals are `Wtf8Atom`, instead of `Atom`
Scaffolds a new API, `unstable_prefetch()`. It's only allowed in server
code, so we ban it from being imported in the client.
Implementation follows upstack. Intended to be merged together.
Scaffolds a new API, `unstable_navigation()`. It's only allowed in
server code, so we ban it from being imported in the client.
Implementation follows upstack. Intended to be merged together.
Adopts #97150. Closes#97150.
Fixes#96859
### What?
Since 16.3.0, builds fail for pages-router files named `sitemap` or
`robots` that export `getStaticProps`/`getServerSideProps`:
```
Error: "getStaticProps" is not supported in app/.
```
This is a regression from #94962, which added the metadata conventions
(`sitemap`, `robots`, `manifest`, `icon`, …) to the app-entry filename
regex in `ReactServerComponentValidator::assert_invalid_api`. The regex
only looks at the filename, so a file like `pages/sitemap.js` is now
mistaken for an app entry and rejected for exporting `getStaticProps`.
### How?
A file is only treated as an app entry when it's inside `appDir`,
reusing the gate `assert_server_filename` already applies to `error.js`.
The pages compilation context has no `appDir`, so pages-router files are
never validated as app entries.
On the test side:
- The fixtures that exercise the app-entry checks moved under
`app-dir/`, since the test harness derives `appDir` from the fixture
path. Their contents are unchanged.
- A new fixture (plus a `sitemap.js` fixture glob) asserts that a
pages-style `sitemap.js` compiles without errors.
- A new e2e suite, `test/e2e/pages-metadata-filenames`, covers
`pages/sitemap.js` with `getStaticProps` and `pages/robots.js` with
`getServerSideProps`. It fails without the fix under Turbopack (build
and dev) and passes with it under both Turbopack and webpack.
Supersedes #96873 (same approach, closed by its author) and closes
#96967.
---------
Co-authored-by: Rodrigo Arias <rodrigo@arias.me>
Under Cache Components, a dynamic route whose `generateStaticParams`
returns an empty array is intentionally an error, but it surfaced badly:
in development the request failed with an uncaught error that left a
blank screen and a 500, and a production build printed the error without
a stack. The cause was that `throwEmptyGenerateStaticParamsError`
deliberately discarded the stack, because the throw happens in framework
code in `buildAppStaticPaths` after the user's function has already
returned, so there was no user frame to point at.
This change adds an SWC transform that gives the error a stack anchored
at the user's code. For a `page`, `layout`, or `default` file that
exports `generateStaticParams`, it emits a
`__next_create_empty_gsp_error` factory whose `new Error` is span-mapped
back to the source. It keys off the export rather than any declaration
named `generateStaticParams`, so it covers `export function`/`export
const`, `export { x as generateStaticParams }`, and `export {
generateStaticParams } from '...'`, while ignoring a same-named local
helper that is never exported. The anchor is the most specific
available: the `return []` literal when it is unambiguously the
function's only return, the declaration, or the export statement when
the body lives in another module. The transform is registered for both
bundlers, gated on Cache Components and excluded from the edge runtime,
mirroring the existing `debug_instant_stack` wiring.
At runtime the factory is attached to the segment as
`createEmptyParamsError` in `collectSegments`, and `buildAppStaticPaths`
throws it when it detects an empty result. Because the error now
originates in the page module rather than framework code, development
surfaces a proper redbox with a meaningful stack and production fails
the build with a stacked CLI error. The custom
`EmptyGenerateStaticParamsError` name has been dropped since it only
added noise to the logs. A few export forms, notably a wildcard `export
*` re-export, can't be detected from the page module alone, so when the
factory is absent the helper falls back to throwing the message with its
natural stack; those framework frames are ignore-listed in the output
but stay available for debugging.
The build-mode error does not yet include a code frame, because
`generateStaticParams` runs in the page-data worker, which lacks the
code-frame support that the prerender export worker has. That is left
for a follow-up. A new end-to-end test covers the redbox in development
for both a literal and a computed empty array, and asserts the per-route
build errors in production using `--debug-build-paths` so each route is
isolated. Transform fixtures cover the literal, computed,
preceding-statement, multiple-return, aliased-export, re-export, and
not-exported cases.
This was allocating a regex on every transform, we can hoist that regex
up to avoid the work
I noticed this as a major source of temporary allocations in a dev
benchmark
also avoid a bunch of tiny string copies
## What?
Replace wasmer with wasmtime as the WASM runtime for SWC plugins.
## Why?
- Wasmer has been a source of build complexity due to conflicting feature flags (`wasmer/default` vs `wasmer/js-default`) and large binary size
- Wasmtime is actively maintained by the Bytecode Alliance with better performance and smaller footprint
- The upstream SWC project already provides `swc_plugin_backend_wasmtime` on crates.io, so we can use it directly instead of maintaining our own implementation
## How?
- Use the upstream `swc_plugin_backend_wasmtime` v9.0.0 crate from crates.io instead of a custom local implementation
- Replace `swc_core/plugin_transform_host_native` (which pulls in wasmer via `__plugin_transform_env_native`) with granular features: `__plugin_transform_host` + `__plugin_transform_host_schema_v1`
- Provide `WasmtimeRuntime` explicitly in both the NAPI bindings and turbopack plugin paths
- Remove all direct `wasmtime`/`wasmtime-wasi` workspace dependencies — the upstream crate manages its own wasmtime version
- Replace wasmer-related release profile size optimizations with equivalent wasmtime ones (`swc_plugin_backend_wasmtime`, `swc_plugin_runner`, `wasmtime`, `wasmtime-internal-cranelift`)
- Pin `vergen = "=9.0.6"` to avoid a `vergen-lib` version conflict with `vergen-gitcl` 1.0.8
## Binary size impact (darwin-arm64, compared against current canary)
| Metric | Canary | Wasmtime (this PR) | Delta |
|---|---|---|---|
| Unstripped dylib | 133.1 MB | 128.2 MB | **-5.0 MB (-3.7%)** |
| Stripped | 91.2 MB | 86.9 MB | **-4.3 MB (-4.8%)** |
| Stripped + gzip (npm) | 30.9 MB | 29.4 MB | **-1.5 MB (-5.0%)** |
<!-- NEXT_JS_LLM_PR -->
### What?
Error messages for the `"use cache"` directive and `after()` API (in
`cookies()`, `headers()`, `connection()`) pointed to `/docs/canary/...`
instead of `/docs/...`.
### Why?
On canary builds, `docs/canary` links can create circular or confusing
references. These APIs are stable and their docs live at the standard
`/docs/app/...` path.
### How?
Replaced all `nextjs.org/docs/canary/` URLs with `nextjs.org/docs/` in:
- `crates/next-custom-transforms/src/transforms/server_actions.rs` — the
`"use cache"` feature flag error
-
`crates/next-custom-transforms/tests/errors/use-cache-not-allowed/1/output.stderr`
and `2/output.stderr` — matching test fixtures
- `packages/next/src/server/request/cookies.ts`, `headers.ts`,
`connection.ts` — `after()` errors
- `packages/next/errors.json` — compiled error message index (6
occurrences)
<!-- NEXT_JS_LLM_PR -->
`unstable_instant` requires `cacheComponents` to function — without it,
validation is entirely skipped and the config silently does nothing.
This adds a compile-time error in the SWC transform that catches the
misconfiguration early, matching the existing pattern used for segment
configs like `revalidate` that are incompatible with `cacheComponents`
(but inverted — `unstable_instant` requires it rather than being
incompatible with it). The error points directly to the export in the
user's source file.
Instead of just limiting the transform to any file within the app dir, we now restrict it to any page, default, or layout file in the app dir. This avoids having the transform applied to random modules. The inserted value would not have been used by anything.
The `wasm` and `next-custom-transforms` crates were still on Rust edition 2018 while the rest of the crates in this repo use edition 2024. This caused a pre-commit hook conflict:
- `cargo fmt` passes the crate's own edition (2018) to `rustfmt`, which uses **case-insensitive** import sorting
- The lint-staged pre-commit hook runs `rustfmt --edition 2024 --`, which uses **ASCII order** import sorting (uppercase before lowercase)
These two sort orders disagree on items like `{custom_before_pass, TransformOptions}` vs `{TransformOptions, custom_before_pass}`, causing an infinite flip-flop: every commit would reorder imports, and the next `cargo fmt` would reorder them back.
## Changes
- Bump `crates/wasm` and `crates/next-custom-transforms` from edition 2018 to 2024
- Add `+ use<C>` precise capturing to `server_actions()` return type to satisfy edition 2024's stricter `impl Trait` lifetime capture rules
- Add `style_edition = "2024"` to `.rustfmt.toml` as a safety net so `cargo fmt` uses 2024-style formatting regardless of individual crate editions
- Reformat all affected files with `cargo fmt`
- Apply new clippy rules
The transform output for 'use cache' uses `Object.defineProperty` and
`Array.prototype.slice`. In theory, it's possible for a module to shadow
those names and break it:
```ts
// i want to suffer!
const Array = {}
const Object = {}
async function cached() {
"use cache"
...
}
```
We should defensively make sure that our transform's output refers to
the actual Object/Array builtins, not whatever's in scope for that name.
We can do this by using an unresolved syntax context for the
identifiers. This is what we do for `require()` elsewhere:
https://github.com/vercel/next.js/blob/6805af48baf1128e6b2a9e8f1752ee5982f02c60/crates/next-custom-transforms/src/transforms/track_dynamic_imports.rs#L119-L122
Using `quote_ident!(unresolved_ctxt, "Array")` will cause SWC to rename
any local redefinitions of `Array` to something that doesn't clash
(which can be seen in the added snapshots).
## What?
Improves the error shown when `generateMetadata` or `metadata` is
exported from a Client Component.
Also updated the documentation to further explain why it can only be
used in a server component and the steps to follow to resolve the error.
Using `taint` APIs from React without enabling `experimental.taint` will
error at runtime because it needs to bundle experimental React, making
it hard to catch and potentially leading to downtime. This updates our
transform to throw an error if we detect that it's imported but `taint`
isn't enabled.
Fixes NAR-690
In #86014 we added a validation for exporting object and array literals
from `'use cache'` files. This was a bit too restrictive, and
short-sighted, as it would prevent exporting common things like metadata
or viewport objects from page and layout files, if the file uses a `'use
cache'` top-level directive.
Previously, in a `'use cache'` module, we only compiled statically-known
exported functions and arrow expressions to cache functions. Now, we
also compile re-exports and exports of values that might be functions.
This includes exports like:
- `export { getData } from './data'` - re-exporting from another module
- `export const aliased = getData` - exporting an imported identifier
- `const Page = withSlug(...); export default Page` - exporting the
result of a function call
These cases can't be statically verified as functions at compile time,
so we generate runtime wrappers with `typeof === "function"` checks
(simplified):
```js
let $$RSC_SERVER_CACHE_getData = getData;
if (typeof getData === "function") {
$$RSC_SERVER_CACHE_getData = React.cache(function() {
return cache("default", "...", 0, getData, arguments);
});
registerServerReference($$RSC_SERVER_CACHE_getData, "...", null);
Object.defineProperty($$RSC_SERVER_CACHE_getData, "name", { value: "getData" });
}
export { $$RSC_SERVER_CACHE_getData as getData };
```
This ensures that all function exports from `'use cache'` modules are
properly cached, while non-function exports pass through unchanged.
This approach comes with some limitations compared to statically-known
function exports:
1. **No compile-time type checking:** We can't verify that these exports
are actually functions. Non-function exports will be passed through
as-is at runtime.
2. **No async enforcement:** We can't enforce that functions must be
async (as we do for statically-known functions). Synchronous functions
are supported, but will lie about their type signature. They become
async after being wrapped as cache functions, even though their original
signature was synchronous.
closes NAR-196
With this PR we are restructuring how the Next.js compiler transforms server function modules (`'use server'` or `'use cache'` directive at the top of the file).
The workaround for skipping the registration of exported `'use cache'` functions is removed. All exported server functions are now properly registered during visitor traversal, which fixes incorrect server reference information bytes in server reference IDs in some circumstances (see #86292 for example).
We're also adding support for string literal exports, e.g.:
```js
'use server'
async function foo() {}
export { foo as '📙' }
```
Upgrade swc from 45 to 48
Based on #85540
- In the AST, strings now contain a `Wtf8Atom` (= not utf8), while `Ident`s keep using the utf8 `Atom`.
- swc's comment printing logic also changed slighted, moving some comments around
- ~~Some comments are getting lost along the way and/or attached to the wrong line. This also broke server actions which rely on a `__next_internal_client_entry_do_not_use__` at the very top~~
When a module does not include any `'use server'` or `'use cache'` closures that use variables from their outer scope, the imports for `encryptActionBoundArgs` and `decryptActionBoundArgs` are unnecessary.
> [!TIP]
> Review with hidden whitespace changes.
When functions exported from a module with a top-level `'use cache'`
directive are imported into client modules, the compiler should allow
`cacheLife` and `cacheTag` usage within those functions.
This PR updates the `react_server_components` transform to properly
track module directives (`'use client'`, `'use server'`, `'use cache'`)
and skip client graph validation for modules with `'use cache'`,
identical to how modules with `'use server'` are already handled. Those
modules are transformed anyway for the client graph by the
`server_actions` transform, which is applied after the
`react_server_components` transform, in which their implementation is
replaced by server function references.
closes NAR-498
Importing `cacheLife` and `cacheTag` in Client Components yields build errors. This adds test fixtures to cover these scenarios.
In addition, we're duplicating the test fixtures that have different error messages between `app/` and `pages/` directories to ensure both cases are covered.
This PR fixes a slight performance regression introduced in #85519 where
inline async function expressions were created for each unique set of
arguments passed to a `'use cache'` function.
For a function like this:
```js
export async function getCachedData(id) {
'use cache';
return fetch(`/api/data/${id}`);
}
```
we were previously generating (simplified):
```js
export var $$RSC_SERVER_CACHE_0 = cache("default", "0815", 0, async function(id) {
return fetch(`/api/data/${id}`);
});
```
In #85519, we restructured this to ensure proper stack frames in error
messages (simplified):
```js
export var $$RSC_SERVER_CACHE_0 = React.cache(function getCachedData() {
return cache("default", "0815", 0, async function(id) {
return fetch(`/api/data/${id}`);
}, arguments);
});
```
However, this introduced a small performance issue: the inner async
function expression was passed inline to the cache wrapper. While
`React.cache` memoizes the outer function, each unique set of arguments
would cause a new inner function object to be allocated.
Now we're generating (simplified):
```js
const $$RSC_SERVER_CACHE_0_INNER = async function getCachedData(id) {
return fetch(`/api/data/${id}`);
};
export var $$RSC_SERVER_CACHE_0 = React.cache(function getCachedData() {
return cache("default", "0815", 0, $$RSC_SERVER_CACHE_0_INNER, arguments);
});
```
The cache implementation is now hoisted to module scope with an `_INNER`
suffix as a const declaration with a named function expression. The
function name is preserved to ensure proper stack traces in dev. This
eliminates the repeated function allocations while preserving the
improvements from #85519.
This moves `experimental.cacheComponents` to a top level config. As part
of this, I disabled some tests in `build-output-prerender` that assert
on `cacheComponents` appearing in the experimental list. In a separate
PR, I'm going to show that Cache Components is enabled next to the
bundler info.
This also updates some docs pages to remove "experimental" language.
In error messages and in docs for `'use cache'` and related APIs, we now
guide users to the `experimental.cacheComponents` config instead of
`experimental.useCache`.
closes NAR-410
Replace construction of `RcStr` instances on constant strings with the `rcstr!` macro which can shift construction costs to compile time and eliminate reference counting.
Performance impacts from all the dropped ref counts are minor but do exist

There is also a small progression in max heap which makes sense, laying out the static rcstrs should be a somewhat easier job compared to mimalloc.
## What?
Rename `experimental.dynamicIO` to `experimental.cacheComponents` across
the Next.js codebase.
## Why?
We're going to be merging the functionality of the `ppr`, `dynamicIO`
and `useCache` experimental flags into the singular `cacheComponents`
flag to reduce complexity of the codebase and simplify adoption for
users wanting to experiment with experimental features.
## How?
- Renamed the configuration option from `experimental.dynamicIO` to
`experimental.cacheComponents`
- Added deprecation handling with automatic migration for the old option
name
- Updated all documentation, tests, and internal references
- Updated Rust code in SWC transforms and Turbopack
- Maintained backward compatibility with deprecation warnings
NAR-158