The `legacyBehavior` prop of the `Link` component has been deprecated
since #77473. For Next.js 16, we're finally removing support for it.
Consequently, we're also removing support for the `passHref` prop, which
was only useful in conjunction with the `legacyBehavior` prop.
A [codemod is
available](https://nextjs.org/docs/app/guides/upgrading/codemods#new-link)
to help you automatically upgrade your codebase.
reverts #77473
---------
Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
Currently, the WebSocket that's used for `next dev` is created in a React hook inside of the `HotReload` component, which is (transitively) rendered by the `ServerRoot` component, but only after the initial RSC response is processed, at least partially.
This creates a problem when integrating React's debug channel, because the root model of the RSC payload will be blocked until the associated debug info has been received through the debug channel. Since we will be using the existing WebSocket connection for the debug channel, we need to establish it before the `ServerRoot` component is rendered to avoid a deadlock.
The next-devtools dispatcher does queue events before React has provided the `dispatch` function in `DevOverlayRoot`. So creating the WebSocket outside of rendering is already supported in that regard.
closes NAR-323
Currently we error if you cannot produce a shell unless you have a
Suspense boundary above the root. This is fine for normal IO but sync IO
like Math.random() and new Date() have much more significant bad
consequences for prerendering. Instead of treating these errors as
another flavor of "must have a shell" validation we should instead treat
them like they must be guarded behind something else dynamic like `await
connection()`.
In addition to unconditionally erroring for Sync IO in Server Components
when prerendering this change also removes the Sync IO warning for
runtime prefetches. This is because at the moment there is no way to
debug these errors in dev. In the future we will add validation for
prefetches and need to add back in some ability to warn for these cases
but until then we will leave this case as a silent deopt.
Don't return the `Result:Err`, but turn into an issue and return `Unparseable`
Also fixes the location translation: Lightning CSS uses 1-based columns, while Turbopack uses 0-based column indices.
The prelude is empty when the entire app is suspended during prerender.
Typically this indicates a lack of suspense boundary in the app, which
creates no static shell, thus throwing error. However, if the root
layout itself is dynamic but wrapped under a suspense, we need special
logic in this PR to detect that.
Prior to this change on every navigation we would validate that the
initial static shell satisified the rules of cache components. This
offers more protection as you edit pages but it slows down navigations
and we are already concerned with dev speed and the extra CPU contention
can potenially hide or exacerbate other performance characteristics you
might notice in dev.
So for now we will only validate a page on initial load. This means that
if you are trying to debug a page's static shell suitability you will
need to refresh the page not just navigate to and from it.
### Improve performance of chunk bootstrapping leveraging arrays and maps
Currently we bootstrap the set of the modules in a chunk by passing an object to the runtime, this is suboptimal for a few reasons:
* The runtime just wants to iterate the object and copy it into a runtime datastructure and iterating objects is relatively slow
* The browser needs to spend time computing the 'shape' of this object and possibly changing internal representations
The solution to both these problems is of course, Arrays!. so instead of an object like
```
{ [moduleId]: (__turbopack_context__) => {},...}
```
we use an array like this
```
[ moduleId, (__turbopack_context__) => {}]
```
* a non-empty sequence of module ids come first
- we may have more than one to support scope hoisted modules
* the factory function
Because these objects are temporary this should make both constructing and iterating them faster.
Finally, in the runtime what we want to do is copy these modules into a global map that is represented as an `Object`. Because we are constantly inserting new keys, this is a suboptimal case and a `Map` should be faster for both insertions and lookups. So this adapts the moduleFactory type to use an es6 `Map` which is more optimized for this particular usecase.
Ideally we would do the same for the moduleCache but that would complicate `require.cache` usecases. We could perhaps leverage a `Proxy` to maintain support but this is left as future work.
### Aside: why are arrays faster than objects?
Arrays are faster to parse because they incur fewer 'shape transitions' (background reading: https://v8.dev/blog/fast-properties vs https://v8.dev/blog/elements-kinds). Basically for every new property v8 parses on the object literal it changes the hidden type (or 'shape'). For arrays the same thing happens but the process is much simpler as there is a fixed set of possible shapes and the browser will hit the terminal one after evaluating the second element.
Also arrays are simply faster to iterate than objects (but this is generally a non-controversial statement).
### Performance
Based on the `module-cost` benchmark this is a consistent win in all scenarios
| Scenario | Metric | HEAD (ms) | CHANGE (ms) | Delta (ms) | % Change |
|----------|--------|-----------|-------------|------------|----------|
| **Pages API ESM** | Load Duration | 33.46 | 27.87 | -5.59 | -16.7% ✅ |
| | Execution Duration | 63.22 | 62.54 | -0.68 | -1.1% ✅ |
| **Pages API CommonJS** | Load Duration | 44.11 | 33.76 | -10.35 | -23.5% ✅ |
| | Execution Duration | 32.05 | 25.70 | -6.35 | -19.8% ✅ |
| **Client ESM** | Load Duration | 34.74 | 27.65 | -7.09 | -20.4% ✅ |
| | Execution Duration | 46.13 | 40.35 | -5.78 | -12.5% ✅ |
| **Client CommonJS** | Load Duration | 30.43 | 24.48 | -5.95 | -19.6% ✅ |
| | Execution Duration | 25.15 | 17.18 | -7.97 | -31.7% ✅ |
### Risks
The biggest risk of this change is subtle bugs introduced due to `string` vs `number` confusion. I found one subtle issue in how we rewrite `new Worker()` constructors, there could be more not covered by tests.
### What?
In development give the module factory a useful name, so it's clear in stack traces that this is the module evaluation part of the execution.
This also fixes some bugs with the stack trace parser, which seems to struggle if function names contain brackets. The automatically inferred function name would be the module id, which contains brackets.
## Optimize ESM Exports
### What
For an esm module like
```js
export const foo = 2;
```
turbopack will generate a factory function like
```js
__turbopack_context__ => {
__turbopack_context__.s({foo: ()=>foo});
const foo = 2;
}
```
* we expose the exports early to enable circular imports between modules
* we expose as 'getter functions' to enable live bindings
These behaviors are necessary to uphold the ESM specification, however, we can optimize it by avoiding objects.
```js
__turbopack_context__ => {
__turbopack_context__.s(['foo', ()=>foo]);
const foo = 2;
}
```
Since the runtime simply walks the object literal in order to call `defineProperty` a number of times switching to an array representation will speed up both construction and iteration.
In a later step i will pursue an approach to avoid generating the getter functions.
### Performance analysis courtesy of v0
I ran the `module-cost` benchmark 30 times across this branch and canary. Each measurement was in a fresh incognito window.
| Scenario | Metric | HEAD (ms) | HEAD σ | HEAD CV | CHANGE (ms) | CHANGE σ | CHANGE CV | Delta (ms) | % Change |
|----------|--------|-----------|---------|---------|-------------|----------|-----------|------------|----------|
| **Pages API ESM** | Load Duration | 39.84 | 2.8 | 7.0% | 36.61 | 2.7 | 7.4% | -3.23 | -8.1% ✅ |
| | Execution Duration | 60.89 | 3.6 | 5.9% | 58.14 | 2.8 | 4.8% | -2.75 | -4.5% ✅ |
| **Client ESM** | Load Duration | 46.09 | 2.4 | 5.2% | 46.31 | 3.2 | 6.9% | +0.22 | +0.5% ❌ |
| | Execution Duration | 47.77 | 0.8 | 1.7% | 46.07 | 1.0 | 2.2% | -1.70 | -3.6% ✅ |
### Key Findings
**Strong improvements in Pages API ESM performance:**
- Load time reduced by 8.1% with consistent variance
- Execution time improved by 4.5% with reduced variance
**Mixed results for Client ESM:**
- Load times remain essentially unchanged (+0.5%)
- Execution times show solid improvement (-3.6%)
- Measurements remain highly consistent across both versions
### What?
Fix the signatures of `useMDXComponents`.
### Why?
The `useMDXComponents` function is always called without any arguments.
It’s also always supposed to be a named export, not a default export.
It’s also called during render. Defining React components during render
is an anti-pattern. All components that were previously defined inside
of `useMDXComponents`, are now defined outside of the function.
### How?
Manual fixes.
---------
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Co-authored-by: Joseph <sephxd1234@gmail.com>
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
When validating a dynamic route in dev with Cache Components we must not
use more params than would be know at build time because we want to
ensure dev acts as an appropriate debug environment for build validation
issues. Any time a validation fails for reasons other than fallback
params it would have failed as well with fallback params. So we find the
smallest set of fallback params that would be used during the build
(which might be empty if you provide a complete set through
generateStaticParams) and we use that.
Implements accessing root params (params segments that occur above root
layouts) via compiler-generated getters from the `next/root-params`
module:
```tsx
// app/[lang]/layout.tsx
// (there is no app/layout.tsx, this is the root layout)
import { lang } from "next/root-params";
// ^^^^ coresponds to the [lang] segment above this module
export default async function RootLayout({ children }) {
return (
<html lang={await lang()}>
<body>{children}</body>
</html>
);
}
```
- This API is only usable within server components (support for using it
in route handlers will be added in the future). It can be called
anywhere in the component tree, not just in a page or a layout.
- It **cannot be used in server actions**, because they're not tied to a
route.
- It will replace `unstable_rootParams`, which will be removed soon.
Note that we can also have multiple root layouts with distinct params,
like this:
```
app/product/[productId]/layout.tsx
app/brand/[brandId]/layout.tsx
```
In this case we'll generate getters for both `productId` and `brandId`.
They can be called anywhere, but they'll return `undefined` if used in a
subtree where a param isn't available.
Note that this PR does not yet generate type declarations for the
generated getters, essentially leaving them typed as `any`. This will be
handled in a follow up.
## Implementation notes
### Turbopack
We collect the root param names when analyzing the folder structure in
`app_structure.rs`. The set is then passed down (as a Vc) all the way to
`next_import_map.rs`, where we insert an alias to a virtual
`next/root-params.js` module. We use a `ImportMapping::Dynamic` to
generate its code lazily in a separate turbo task (which is the only
place that actually reads the root params). This avoids invalidating the
whole `ResolveOptionsContext` if the root params change.
### Webpack
We alias `next/root-params` to `next-root-params-loader`, which scans
the directory structure manually (i haven't found a good way to re-use
existing methods for doing this) and returns the generated module. Each
directory we traverse before finding a root layout is marked as a
`contextDependency`, which should invalidate the loader's result if new
files are added to any of them or if they get renamed, because it might
mean that the set of root params changed.
### What?
* use prototype for turbopack context for better runtime performance
* remove destructuring when unneeded
* ensure all calls use the correct context
* create static module object at one location
### Why?
Improve performance by reducing allocations in the hot path of module evaluation:
Based on our module-cost benchmark this is about a 15% runtime improvement
* CommonJS Execution Duration: 25.60ms → 21.85ms (14.6% improvement)
* ESM Execution Duration: 53.34ms → 44.82ms (16.0% improvement)
Which addressed most of the known gap with webpack.
Part of PACK-5057
This PR is a perf improvement that we only need to load Next.js config once per process. For config schema validation we also only execute once. I noticed that config-schema is executed 3 times, in the 1st root process, and twice when the router-server load configs.
* For `loadConfig` it needs module loading, config parsing, save the expensive effort to only execute once
* Always validate config schema if possible and don't be silent, but only do it once is the parent process. We use `process.send` to check if it's in the root or forked process.
## Always format layer names in import trace headers
Previously if there was a single import trace where all items were in a single layer we wouldn't display the layer. With this we always put the layer in the trace 'header' for a single trace just like we do when there are multiple traces (unless there are no layers which is really just a theoretical possibility).
### Why?
The concern was really about consistency
Tim was [confused](https://vercel.slack.com/archives/C03KAR5DCKC/p1752584756904199?thread_ts=1749510128.195809&cid=C03KAR5DCKC) why the layer headers showed up sometimes but not others. Obviously when there are multiple traces, the layers are a critical distinguishing factor, when there is a single one we might suspect it is obvious, but inferring the layer of a file isn't always and by consistently formatting traces the same way we can ease readability.
Also add some unit tests to speed up iteration on this formatting.
We just need to give a special name to that module so that we can ignore-list it later.
For Webpack I chose the same approach as https://github.com/vercel/next.js/pull/81229. Turbopack already sourcemapped the generated module.
Closes https://linear.app/vercel/issue/NAR-234
RSPack team is informed. Their compat layer doesn't implement `createFilename`
## 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
When we updated `experimental.strictNextHead` to be true in #65418, we
did not update all spots that would default to true in the case where
the value was omitted entirely.
This led to the default value not being correctly applied in pages
router, which resulted in duplicate metadata w/ React 19.
Since we made the flag the default, we can also probably clean up this
flag all together, but that can be done separately.
Fixes#81655Fixes#81689
Format line format: `<segment> [<files,>]`
This will help visualize the rendered segment explorer panel easier by checking the snapshot. e.g.
```
app/ [layout.tsx]
parallel-routes/ [layout.tsx, page.tsx]
@bar/ [layout.tsx, page.tsx]
@foo/ [layout.tsx, page.tsx]
```
Closes NEXT-4534
This PR introduces the ability for next.js to forward logs, errors, and
unhandled rejections from the browser to the terminal the dev server is
running in (behind an experimental flag)
# Explanation
The 2 main components of this pr are the client side error accumulation
logic, and the ingest handling on the other side of the hmr socket.
We listen on the existing hmr socket to send batched logs, errors, and
uncaught rejections the frame after they were captured. All forwarded
data is sent with metadata so we can have reconstruct the log with an
equivalent level of information to the browser- since we expect AI
agents that can't access the browsers to be consumers of this feature
(and it's generally useful).
All foreign data created by the user in the browser is serialized using
`safe-stable-serializer`, a popular serializer [used by other logging
libraries](https://www.npmjs.com/browse/depended/safe-stable-stringify),
like
[pino](https://github.com/search?q=repo%3Apinojs%2Fpino+safe-stable&type=code)
(safety, determinism, fast). We also have a light shim on top of json
serialization to handle displaying custom data representations that
either wouldn't survive serialization (undefined) or we want to present
to users in a custom format (throwing proxies, promises, ...)
On the dev server server, we (bespoke) deserialize, source map, format,
and log. I tried to share as much logic as I could with error dev
overlay to avoid feature drift since they are very similar
implementations other than the render target
# Explicitly covered cases
- console table
- shows as `[browser]\n<table>\n(<source location>)`
- console trace
- shows as `[browser] arg1 arg2 ...\n<stack trace>\n([source mapped
location of log]`)`
- trace is source mapped
- ignored frames are shown, incase people explicitly want the full trace
- console dir
- shows as `[browser] arg1 arg2 ([source mapped location of log])`
- we need to explicitly capture stdout and rewrite it when we call nodes
`console.dir` to prefix and postfix with [browser] and and (`<source
mapped location of log>`) without adding newlines (we could do this for
console.table but it makes sense to keep the prefix and postfix on new
lines)
- console error
- `[browser] arg1 arg2 \n codeblock + source mapped stack of
console.error ([source mapped location of console.error])`
- if there are any `Error` values present we wont show the stack and
code block of console.error since it's overwhelming (this is fine since
we still tell the user where the `console.error` is with the appended
location)
- rejected promises that have `Error`'s
- behave identical to console.error, but is prepended with `⨯
unhandledRejection`
- the `Error`'s render with their source mapped stack + ignored frames
- no error stack can be automatically appended where the promise
rejected
- rejected promises that have non `Error` values
- prepended with `⨯ unhandledRejection: ${error.name}: ${error.message}`
- everything is logged in red
- no error stack can be automatically appended where the promise
rejected
- on caught error
- prepended with `Uncaught ${errorName}: ${errorMessage}`
- stack attached to error is source mapped + ignored frames are not
shown
- everything is logged in red but the code block of where the error
orginated from
- all other console cases
- shows as `[browser] arg1 arg2 ([source mapped location of log])`
- if an error is passed, we show the error name, message, source mapped
stack (colored white, ignored frames not shown), and code block if
available (syntax highlighted)
- we apply util.format to handle formatted strings
- logs captured during RSC rendering
- not piped to server, ignored on client
Closes NEXT-4534
Capture defined boundaries (not-found / error / loading) and used to filter which one is available for boundary triggers
The trie will now hold all the defined boundaries, but what's different from the existing rendered boundaries is:
Here's the difference of new defined boundary trie nodes:
```
{
type: "boundary:<type>"
pagePath: "<pagePath>@boundary"
}
```
We add a `boundary:` prefix to determine if it's node representing existence;
We add a `@boundary` suffix to the filename cause the <pagePath> part is still need to use for indexing in the trie, but the `@boundary` doesn't matter yet. We'll remove it once we start using the file name in the dropdown panel
Closes NEXT-4330
### Example
When there's only one not-found.tsx, allow to trigger the not-found boundary and disable the rest.
https://github.com/user-attachments/assets/eb5c49a7-4fd2-496b-b778-fb66581908b3
<!-- 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
### Adding or Updating Examples
- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md
### 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
## 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 #
-->
update Rspack to 1.4.2, in this version we fix bugs when using Rspack
persistent cache in next.js.
Now user can try Rspack persistent cache by follow way:
```js
const config = {
webpack: (config) => {
config.experiments.cache = {
type: 'persistent'
};
return config;
},
};
```