33314 Commits

Author SHA1 Message Date
nextjs-bot ed7d2cef24 v16.2.1 v16.2.1 2026-03-20 23:23:58 +00:00
Joseph 3e37bb42d2 docs: post release amends (#91715)
Fixes based on feedback post release

Fixes: https://github.com/vercel/next.js/pull/91715
2026-03-20 15:01:21 -07:00
Abhishek Mardiya a15ec6e970 docs: fix broken Activity Patterns demo link in preserving UI state guide (#91698)
## What?

Fixes a broken link in the Preserving UI State guide:
https://nextjs.org/docs/app/guides/preserving-ui-state#examples

The "Activity Patterns demo" link currently points to a non-working URL.

## Why?

The demo link returns a 404 / invalid page, which makes the example
section confusing for readers.

## Changes

- Updated the Activity Patterns demo link to the correct URL.

## Notes

No functional changes, docs only.

@icyJoseph @delbaoliveira
2026-03-20 15:01:21 -07:00
JJ Kasper 600cd2fedb Fix adapter outputs for dynamic metadata routes (#91680)
## Summary

This fixes adapter `onBuildComplete` outputs for dynamic metadata
routes.

## Bug

`handleBuildComplete` skipped metadata routes too aggressively via
`isStaticMetadataFile(...)`, so dynamic metadata routes (for example
`robots.txt` / `sitemap.xml` using `connection()`) were omitted from
`outputs.appRoutes`.

## Fix

Only skip metadata routes when they are actually prerendered/static
(present in prerender manifests). Dynamic metadata routes are now
included in adapter `outputs.appRoutes` as expected.

## Tests

Added e2e coverage in `test/e2e/app-dir/adapter-dynamic-metadata`:
- verifies dynamic `robots.txt`, `sitemap.xml`, and `favicon.ico`
functionality
- verifies adapter output classification for non-deploy runs
- skips output-shape verification in deploy mode
- skips the suite in dev mode
2026-03-20 14:30:40 -07:00
Niklas Mischkulnig 27886d3cfb Turbopack: fix webpack loader runner layer (#91727)
Otherwise you get a `Dependency tracking is disabled` / infinite loop
when trying to use both turbopack.rules.loader and turbopackLoader at
the same time
2026-03-20 14:30:40 -07:00
Hendrik Liebau 88fc4308e2 Fix server actions in standalone mode with cacheComponents (#91711)
The `staticPathKey` condition added in #91231 inadvertently applies to
server action requests on dynamic SSG routes when `cacheComponents` is
enabled.

Server action fetch requests from the client do not send the `RSC`
header (`rsc: 1`). They only send `Accept: text/x-component` and the
`Next-Action` header. This means `isRSCRequest` and
`isDynamicRSCRequest` are both `false` for action requests. The new
`staticPathKey` condition (`isSSG && pageIsDynamic &&
prerenderInfo?.fallbackRouteParams`) evaluates to `true` for dynamic PPR
routes, setting `staticPathKey` even though `ssgCacheKey` is `null` for
actions.

With `staticPathKey` set, the request enters the fallback rendering
block, which serves the cached fallback HTML shell with the action
result appended to it, instead of responding with just the RSC action
result.

The fix excludes server action requests from the `staticPathKey`
computation by adding `!isPossibleServerAction` to the condition,
restoring the pre-#91231 behavior where `staticPathKey` was always
`null` for server actions in production.

fixes #91662
closes #91677
closes #91669
2026-03-20 14:30:40 -07:00
Tobias Koppers 37aed862ed turbo-persistence: remove Unmergeable mmap advice (#91713)
Remove the `MADV_UNMERGEABLE` (Unmergeable) mmap advice from
`turbo-persistence`'s `advise_mmap_for_persistence` helper in
`mmap_helper.rs`.

The WSL (Windows Subsystem for Linux) kernel does not support
`MADV_UNMERGEABLE` and returns an error when it is applied, causing
turbo-persistence to fail on WSL. See:
https://x.com/von_cronen/status/2034944304712392891

Additionally, `MADV_UNMERGEABLE` is already the **default** state for
memory pages — KSM (Kernel Same-page Merging) is opt-in via
`MADV_MERGEABLE`. Explicitly setting `MADV_UNMERGEABLE` on pages that
were never marked mergeable is a no-op on standard kernels, so the call
provides no benefit while breaking WSL users.

The `MADV_DONTFORK` advice is retained, as it has a clear correctness
benefit (prevents mmap regions from being inherited by child processes
on `fork()`).

Removed the `mmap.advise(memmap2::Advice::Unmergeable)` call and its
associated doc comment from
`turbopack/crates/turbo-persistence/src/mmap_helper.rs`.

Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-20 14:30:34 -07:00
Tobias Koppers d6195eca8c Fix layout segment optimization: move app-page imports to server-utility transition (#91701)
## Summary

- Add `'turbopack-transition': 'next-server-utility'` to all server-side imports in the `app-page.ts` template so they are properly placed in the server utility layer, rather than relying on `SharedMerged` chunk groups to handle them
- Remove the now-unnecessary `next-server-utility` transition from `fillMetadataSegment` import in `app_page_loader_tree.rs`
- Add `SharedMultiple` chunk group variant to support multiple shared entries without a merge tag
  - This avoid passing the `parent` into `SharedMerged`, which did break the layout segment optimization.
- Refactor `app_module_graphs` to combine `client_shared_entries` + `has_layout_segments` into a single `Option` parameter, simplifying the API and removing the dead code path for non-page endpoints

## Test plan

- [ ] Verify dev and production builds work with app pages that have layout segments
- [ ] Verify route handlers still build correctly (no client runtime entries passed)
- [ ] Run existing app-dir e2e tests
2026-03-20 14:30:00 -07:00
Niklas Mischkulnig 6cb97d6d75 Turbopack: lazy require metadata and handle TLA (#91705)
A regression from #88487

1. Make metadata import lazy with `() => require()`, just like for the layout segments
2. Properly await the return value to better handle TLA modules

This align with Webpack which does this:

<img width="1535" height="509" alt="Bildschirmfoto 2026-03-20 um 11 58 00" src="https://github.com/user-attachments/assets/f2864c86-ccce-4884-8417-c8ae06c05f78" />


Closes PACK-6927
Closes #91700
Closes https://github.com/vercel/next.js/issues/91676
2026-03-20 14:28:49 -07:00
Luke Sandberg e6b101ae71 [turbopack] Respect {eval:true} in worker_threads constructors (#91666)
### What?

Fixed Turbopack incorrectly trying to resolve inline JavaScript code as module references when `new Worker()` is called with `{ eval: true }` option.

Now we skip creating a reference when `eval:true`, and report warnings if we cannot tell what the value is

### Why?

Libraries like jsPDF create Worker threads by passing inline JavaScript code as the first argument along with `{ eval: true }` as the second argument. Turbopack was incorrectly treating this inline code as a file path and attempting to resolve it as a module reference, causing build failures.

### How?

Added logic to detect when the `eval: true` option is passed to the Worker constructor. When this option is present, Turbopack now skips creating a worker reference since the first argument contains executable code rather than a file path. Added comprehensive test coverage using jsPDF to verify the fix works correctly.

Fixes #91642
2026-03-20 14:28:49 -07:00
nextjs-bot 0aa15fdfe2 setup release branch 2026-03-18 18:55:15 +00:00
nextjs-bot c5c94dffbf v16.2.0 v16.2.0 2026-03-18 17:10:38 +00:00
Hendrik Liebau 649d302635 Unflake router events deploy test (#91589)
[Flakiness
metrics](https://app.datadoghq.com/ci/test/runs?query=test_level%3Atest%20%40git.repository.id%3A%22github.com%2Fvercel%2Fnext.js%22%20%40test.status%3A%22fail%22%20%40test.name%3A%22basePath%20should%20use%20urls%20with%20basepath%20in%20router%20events%20for%20hash%20changes%22&agg_m=count&agg_m_source=base&agg_t=count&currentTab=overview&eventStack=&fromUser=false&index=citest&start=1773244604685&end=1773849404685&paused=false)

After upgrading Playwright in #91250 (which brought some performance
improvements), some tests are now exposed to be missing retries around
assertions. `test/e2e/basepath/router-events.test.ts` is one of those
tests, and this PR adds a `retry` around the event log assertion. We're
also replacing the deprecated `check` utility with `retry` when waiting
for the router to be ready.
2026-03-18 16:34:53 +00:00
Joseph bcd9c19a61 docs: Clarify ignoreBuildErrors behavior (#91367)
Just a complementary note to the TS config reference.
2026-03-18 16:54:42 +01:00
nextjs-bot 3683192a6b v16.2.0-canary.104 v16.2.0-canary.104 2026-03-18 13:29:38 +00:00
Richard Zhang b61823d7b9 SRI turbopack documentation (#90477)
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
Updates the Subresource Integrity (SRI) documentation to reflect that
the feature is now supported on both Webpack and Turbopack bundlers.

Previously, the documentation stated that SRI was "only available with
webpack bundler". This has been updated to remove that restriction
following the implementation in PR #90152.

### Changes
- Removed webpack-only limitation from SRI documentation in Content
Security Policy guide
- Removed `experimental.sri.algorithm` from the list of unplanned
Turbopack features

Fixes the documentation to align with the current implementation.
<!-- CURSOR_AGENT_PR_BODY_END -->

[Slack
Thread](https://vercel.slack.com/archives/D0AGHCN7K2T/p1771984911390169?thread_ts=1771984911.390169&cid=D0AGHCN7K2T)

<div><a
href="https://cursor.com/agents/bc-0b4d6ed8-9e06-5c78-9a4d-b352a7c2e97d"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-0b4d6ed8-9e06-5c78-9a4d-b352a7c2e97d"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Richard Zhang <richardjzhang@users.noreply.github.com>
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
2026-03-18 09:59:35 +01:00
Josh Story 0ca967b863 Add group depth tracking to instant validation boundary discovery (#91208)
The depth-based instant validation previously only iterated
URL-consuming segments as potential shared/new boundaries. Route groups
between URL segments were invisible — when a route group layout with
Suspense was shared in a client navigation, its Suspense appeared to
cover blocking code in the validation render even though it wouldn't in
reality.

This adds a second dimension to the validation iteration: group depth.
At each URL depth, we now also step through route group boundaries. A
new `discoverValidationDepths` function walks the LoaderTree and returns
an array where each entry represents the max group depth at that URL
depth. The validation loop iterates from deepest group depth to
shallowest within each URL depth, stopping on the first error.

Key design decisions:
- URL depth and group depth counters are advanced before the boundary
check, making the boundary condition a simple `nextUrlDepth > depth &&
currentGroupDepth >= groupDepth`
- The synthetic `(slot)` segment that Next.js inserts for parallel slots
is excluded from group depth counting since it doesn't represent a real
navigation boundary
- Group depths are discovered from the LoaderTree rather than the URL
pathname, making the tree the source of truth for validation bounds
2026-03-17 22:14:19 -07:00
Zack Tanner a41bef94c5 improve allowedDevOrigins error (#91521)
This improves the blocked-request warning so it names the actual dev
resource being requested and gives clearer guidance on how to allow it.
When the source host is known, the message includes an inline
`allowedDevOrigins` config snippet; when the source is missing or
opaque, it explains why Next.js cannot infer a host to allow.
2026-03-17 17:35:29 -07:00
Benjamin Woodruff 75c51c6541 Turbopack: Use debug = "line-tables-only" for dev builds (#91539)
This defaults to `true`: https://doc.rust-lang.org/cargo/reference/profiles.html#debug

Nobody on the team uses a debugger often enough for this to be worth the time tradeoff: https://kobzol.github.io/rust/rustc/2025/05/20/disable-debuginfo-to-improve-rust-compile-times.html

I'm gathering some rough numbers, and I'll add them to the PR.

After `cargo clean` and one `cargo build -p next-napi-bindings` with each profile (Linux arm64):

```
du -sh target/*
4.0K    target/CACHEDIR.TAG
7.9G    target/debug
14G     target/dev-with-debug
7.5M    target/generated
```

This is nearly half the size.

Due to cargo quirks, the `dev` profile defaults to the `target/debug` directory. Of course, any disk size wins you get are more than offset if you need to use a debugger and end up with an additional profile.

```
hyperfine -w 1 -r 5 -p 'cargo clean' 'cargo build --profile dev-with-debug -p next-napi-bindings' 'cargo build -p next-napi-bindings'
```

...pending...

(this is on https://github.com/bgw/benchmark-scripts/, on my M2 Macbook inside of a Linux VM this takes closer to 2m20s)
2026-03-17 17:20:12 -07:00
Benjamin Woodruff 39e705ca9f Turbopack: Merge release-with-assertions-no-lto profile into release-with-assertions (#91530)
- More profiles means more bloat to the `target` directory and more cold builds.
- There's no practical use case where we'd be okay with the performance hit of `debug-assertions` and not okay with the performance hit of `lto = false`. Neither profile was suitable for benchmarking.
- `release-with-assertions-no-lto` is too long to type and was giving me carpal tunnel.
2026-03-17 16:36:59 -07:00
Luke Sandberg 4de2201764 [turbopack] Share scratch buffer across shards using thread local (#90167)
Instead of allocating a scratch buffer for every shard, buffer them in a thread local.
2026-03-17 16:31:42 -07:00
nextjs-bot 668981482a v16.2.0-canary.103 v16.2.0-canary.103 2026-03-17 23:26:26 +00:00
Benjamin Woodruff 489eaef21e docs(turbopack): Remove old architecture.md file from turbopack crate (#91473)
All the stuff in this file is either better documented elsewhere or out-of-date and misleading.
2026-03-17 16:24:52 -07:00
Zack Tanner b2b802c043 block disallowed dev origins by default (#91507)
This removes the warn-only default behavior and enforces the dev-origin guard by default. Cross-origin requests to internal dev resources now block unless they match the built-in local allowlist or an explicit `allowedDevOrigins` entry. The tests are expanded to cover default blocking, configured-but-not-allowlisted hosts, missing Referer in the no-cors path, and same-site requests without an Origin, and the docs are updated to match the new behavior.
2026-03-17 16:02:33 -07:00
Tim Neutkens 97f420911d Move adapterPath from experimental to stable top-level config (#91535)
## Move `adapterPath` from experimental to stable

Graduates `adapterPath` from `experimental.adapterPath` to a top-level
`NextConfig` option, following the same pattern used for
`cacheHandlers`, `cacheComponents`, `typedRoutes`, and other options
that moved out of experimental.

### Changes

- **Type definition** (`config-shared.ts`): Added `adapterPath` to
`NextConfig` interface, marked `ExperimentalConfig.adapterPath` as
`@deprecated`, moved default value to top-level in `defaultConfig`,
updated `NextConfigRuntime` and `getNextConfigRuntime`
- **Zod schema** (`config-schema.ts`): Added `adapterPath` to top-level
`configSchema`
- **Backward compat** (`config.ts`): Added
`warnOptionHasBeenMovedOutOfExperimental` call so existing
`experimental.adapterPath` configs still work with a deprecation
warning, updated `applyModifyConfig` to read from `config.adapterPath`
- **Runtime references**: Updated `build/index.ts`, `app-page.ts`,
`app-route.ts`, `pages-handler.ts` to use `config.adapterPath` /
`nextConfig.adapterPath`
- **Tests**: Updated 4 test fixture `next.config` files
- **Docs**: Updated app router doc (removed `version: experimental`
frontmatter, top-level config example), pages router doc title, and v16
upgrade guide
2026-03-17 23:28:08 +01:00
nextjs-bot 668f8e3a60 Upgrade React from c80a0750-20260312 to 3f0b9e61-20260317 (#91522) 2026-03-17 22:37:18 +01:00
Tobias Koppers ee1f9d79ad Remove webpack stats generation from Turbopack (#91515)
### What?

Remove the `TURBOPACK_STATS` feature that generated webpack-compatible
`webpack-stats.json` files from Turbopack's output.

**Deleted:**
- `crates/next-api/src/webpack_stats.rs` — the entire 305-line Rust
implementation that built webpack-format stats (assets, chunks, modules,
entrypoints) from Turbopack's module graph

**Rust changes:**
- `crates/next-api/src/lib.rs` — removed `mod webpack_stats;`
- `crates/next-api/src/project.rs` — removed
`should_create_webpack_stats()` which read the `TURBOPACK_STATS` env var
- `crates/next-api/src/pages.rs` — removed `generate_webpack_stats`
import and stats output emission
- `crates/next-api/src/app.rs` — same

**TypeScript changes:**
- `packages/next/src/shared/lib/constants.ts` — removed `WEBPACK_STATS =
'webpack-stats.json'` constant
- `packages/next/src/shared/lib/turbopack/manifest-loader.ts` — removed
`loadWebpackStats()`, `writeWebpackStats()`, `mergeWebpackStats()`, the
`webpackStats` field, webpack type imports, and the `TURBOPACK_STATS`
env guard in `writeManifests()`
- `packages/next/src/server/dev/turbopack-utils.ts` — removed
`shouldCreateWebpackStats` flag and two `loadWebpackStats()` call sites
- `packages/next/src/build/handle-entrypoints.ts` — same

### Why?

This feature is no longer needed and adds dead complexity. It was opt-in
via `TURBOPACK_STATS=1` but nothing in the current codebase or tooling
depends on it.

### How?

Pure deletion — no behavior change for any existing user, as the feature
was opt-in via an environment variable that is no longer checked.

**Intentionally left untouched:**
- Webpack's own `stats.json` generation (non-Turbopack path)
- The Turbopack bundle analyzer / `analyze` feature
- `route-bundle-stats.json` in `.next/diagnostics/`

Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-17 13:58:51 -07:00
Zack Tanner d0a0474d3d fix allowedDevOrigins for no-cors requests (#91506)
This PR makes configured `allowedDevOrigins` apply to cross-site no-cors dev asset requests. When browsers omit Origin for subresource loads, the dev guard now falls back to `Referer` so explicit allowlisted hosts can load `/_next/*` resources in development.

Previously, when `allowedDevOrigins` was configured, cross-site no-cors requests to internal Next.js dev resources were still blocked even for allowlisted hosts, because that code path never consulted the allowlist.
2026-03-17 13:57:40 -07:00
Hendrik Liebau 89c6e73c81 Unflake two "use cache" tests (#91529)
[Flakiness
metric](https://app.datadoghq.com/ci/test/runs?query=test_level%3Atest%20%40git.repository.id%3A%22github.com%2Fvercel%2Fnext.js%22%20%40test.name%3A%28%22use-cache%20should%20cache%20results%20for%20cached%20functions%20imported%20from%20client%20components%22%20OR%20%22use-cache%20should%20cache%20results%20for%20cached%20functions%20passed%20to%20client%20components%22%29%20%40test.status%3A%22fail%22%20%40duration%3A%3E%3D1ns&agg_m=count&agg_m_source=base&agg_t=count&citest_explorer_sort=timestamp%2Casc&cols=%40test.status%2Ctimestamp%2C%40test.suite%2C%40test.name%2C%40duration%2C%40test.service%2C%40git.branch&currentTab=overview&eventStack=&fromUser=false&index=citest&start=1772476060657&end=1773772060657&paused=false)

I'm not sure why these tests recently got much more flaky (they were
somewhat flaky before), but they definitely were missing `retry` blocks
around the assertions after clicking the reset button.
2026-03-17 12:47:27 -07:00
Tobias Koppers 4494261bd7 Remove devCacheControlNoCache experimental option (hard-code no-cache) (#91503)
### What?

Removes the `experimental.devCacheControlNoCache` config option entirely
and hard-codes `no-cache, must-revalidate` as the dev server
`Cache-Control` header value.

Previously the option controlled whether the dev server responded with:
- `no-store, must-revalidate` (default, `false`)
- `no-cache, must-revalidate` (opt-in, `true`)

This PR first flips the default to `true`, then removes the option
altogether — making `no-cache, must-revalidate` unconditional in all dev
code paths.

### Why?

`no-cache` is strictly better than `no-store` for the dev server:
- `no-cache` allows the browser to revalidate (conditional
`If-None-Match`/`If-Modified-Since` requests), letting the server
respond with `304 Not Modified` when nothing changed → faster page loads
during development.
- `no-store` forces a full re-fetch every time, discarding valid cached
responses.

Since `no-cache` is the correct behavior for all dev users, the toggle
has no remaining value and can be removed to simplify the codebase.

### How?

**Two commits:**

1. `bcec825` — Flip the default from `false` → `true`; update
tests/fixtures/manifest to reflect the new default.

2. `e6e919f` — Remove the option entirely:
- Deleted `devCacheControlNoCache?: boolean` from `ExperimentalConfig`
interface, `config-schema.ts` Zod schema, `defaultConfig`,
`NextConfigRuntime`, and `getNextConfigRuntime()`.
- Replaced all four conditional ternaries in `base-server.ts`,
`router-server.ts`, `pages-handler.ts`, and `app-page.ts` with the
hard-coded string `'no-cache, must-revalidate'`.
- Deleted the `dev-cache-control-no-cache-disabled` test suite (was
testing the `false` path which no longer exists).
- Simplified the remaining `dev-cache-control-no-cache` test (removed
experimental framing).
   - Synced `rspack-dev-tests-manifest.json`.

---------

Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2026-03-17 20:13:01 +01:00
Luke Sandberg 4050a5b09c [turbopack] update qfilter to 0.3.0-alpha (#91467)
## What?

Update the `qfilter` crate to an alpha version.

## Why?

Performance improvements in the quotient filter implementation used by turbo-persistence for SST file lookups.

## Benchmark Results

Benchmarks run on Apple Silicon (M-series), comparing `canary` baseline vs this branch. Focused on qfilter-sensitive paths: the filter itself, SST lookups, uncompacted multi-commit DB reads, and writes.

### qfilter microbenchmarks (direct filter operations)

| Benchmark | canary | branch | change |
|---|---|---|---|
| **Lookup (hit)** | | | |
| 1Ki entries | 22.10 ns | 14.63 ns | **-33.8%** |
| 10Ki entries | 22.21 ns | 15.41 ns | **-30.6%** |
| 100Ki entries | 24.96 ns | 17.47 ns | **-30.0%** |
| 1000Ki entries | 25.05 ns | 16.61 ns | **-33.7%** |
| **Lookup (miss)** | | | |
| 1Ki entries | 12.09 ns | 9.19 ns | **-24.0%** |
| 10Ki entries | 14.24 ns | 11.07 ns | **-22.3%** |
| 100Ki entries | 18.13 ns | 13.72 ns | **-24.3%** |
| 1000Ki entries | 13.36 ns | 10.00 ns | **-25.2%** |
| **Insert (build filter)** | | | |
| 1Ki entries | 8.54 us | 9.64 us | +12.9% |
| 10Ki entries | 118.16 us | 120.97 us | +2.4% |
| 100Ki entries | 3.18 ms | 2.57 ms | **-19.2%** |
| 1000Ki entries | 20.88 ms | 18.31 ms | **-12.3%** |

**Summary:** Lookups 22-34% faster across all sizes. Insert is slightly slower at small sizes but 12-19% faster at larger sizes where it matters most.

### SST file lookup (filter + block read)

| Benchmark | canary | branch | change |
|---|---|---|---|
| **Hit (uncached)** | | | |
| 1Ki entries | 2.52 us | 2.52 us | ~0% |
| 10Ki entries | 3.60 us | 3.52 us | -2.2% |
| 100Ki entries | 3.77 us | 3.72 us | -1.3% |
| 1000Ki entries | 6.65 us | 6.55 us | -1.5% |
| **Miss (cached)** | | | |
| 1Ki entries | 124.50 ns | 121.13 ns | -2.7% |
| 10Ki entries | 168.18 ns | 161.96 ns | **-3.7%** |
| 100Ki entries | 195.97 ns | 189.80 ns | **-3.1%** |
| 1000Ki entries | 249.85 ns | 235.68 ns | **-5.7%** |

**Summary:** Small but consistent improvements across SST lookups. Miss/cached shows the clearest gains (filter is the primary code path for rejecting misses).

### DB-level reads (20 commits, uncompacted -- amplifies filter cost)

| Benchmark | canary | branch | change |
|---|---|---|---|
| **10.67Mi entries** | | | |
| hit/uncached | 3.49 us | 3.13 us | **-10.3%** |
| hit/cached | 1.65 us | 1.49 us | **-9.7%** |
| miss/uncached | 899.75 ns | 746.28 ns | **-17.1%** |
| miss/cached | 793.51 ns | 668.92 ns | **-15.7%** |
| **85.33Mi entries** | | | |
| hit/uncached | 11.24 us | 10.64 us | ~0% (noisy) |
| hit/cached | 4.53 us | 4.10 us | **-9.5%** |
| miss/uncached | 5.01 us | 4.36 us | **-13.0%** |
| miss/cached | 4.81 us | 4.02 us | **-16.4%** |

**Summary:** 10-17% faster reads on uncompacted DBs with many SSTs. Miss paths benefit most since the filter rejects without I/O.

### Write path (includes filter construction)

| Benchmark | canary | branch | change |
|---|---|---|---|
| 85.33Ki entries | 24.91 ms | 23.42 ms | -6.0% |
| 853.33Ki entries | 140.63 ms | 131.84 ms | **-6.3%** |
| 8.33Mi entries | 1.14 s | 1.07 s | **-6.1%** |

**Summary:** ~6% faster writes across all sizes.

## How?

Updated the `qfilter` dependency to a new alpha version with improved lookup and insert performance.
2026-03-17 12:08:50 -07:00
Jiwon Choi 6db019c330 Skip catch-error-react-compiler test to unblock CI (#91528)
https://github.com/vercel/next.js/actions/runs/23170738194/job/67327462789#step:36:318
is holding up CI, including release. Cause is not from `catchError` PR,
it's just that the test has discovered missing support of Next.js for
Pages + React 17/18 + React compiler. Will work in parallel.
2026-03-17 19:55:55 +01:00
Sebastian "Sebbie" Silbermann c5577bb38e [react-sync] Allow locking in manual sync (#91509) 2026-03-17 18:23:39 +00:00
Benjamin Woodruff 2d44d0dcd1 docs(turbopack): Migrate chunking docs from mdbook, with proof-reading and minor updates from claude (#91472)
Copied from https://turbopack-rust-docs.vercel.sh/turbopack/chunking.html

I had claude review it for correctness against the code. It found a few things where the names weren't correct or a trait was actually a struct, and it grouped things into sections. It also added some more details to a few sections, like the dev vs production chunking. I think this is a reasonable improvement.

I did a second pass with claude where I had it remove things it 1:1 duplicated with the existing rustdocs, and I had it add intradoc links.

Rendered:

![Screenshot 2026-03-16 at 14-40-34 turbopack_core _chunking - Rust.png](https://app.graphite.com/user-attachments/assets/1112b010-1d38-4604-b7f9-ec29816565b2.png)
2026-03-17 10:59:31 -07:00
Andrew Clark 568c7029ab Track vary params during runtime prefetches (#89297)
**Previous:**

1. https://github.com/vercel/next.js/pull/91487
2. https://github.com/vercel/next.js/pull/91488

**Current:**

3. https://github.com/vercel/next.js/pull/89297

---

Most of the vary params infrastructure was already implemented in
previous PRs for static prerenders. This wires up the remaining pieces
for runtime prefetches — creating the accumulator, setting it on the
prerender store, and resolving it before abort — and adds additional
test cases covering empty/full vary sets, searchParams, metadata, and
per-segment layout/page splits with runtime prefetching.
2026-03-17 13:58:23 -04:00
Andrew Clark e16f6fb2f3 [Segment Cache] Always upsert on prefetch completion (#91488)
**Previous:**

1. https://github.com/vercel/next.js/pull/91487

**Current:**

2. https://github.com/vercel/next.js/pull/91488

**Up next:**

3. https://github.com/vercel/next.js/pull/89297

---

When a prefetch response includes vary params, the segment cache rekeys
entries to a more generic path based on which params the segment
actually depends on. Previously, the rekeying only happened when vary
params were provided. Now that vary params are tracked for more response
types (and eventually will always be tracked), entries are rekeyed in
more cases than before.

This exposed a potential race condition: the scheduler would capture a
vary path at scheduling time and upsert the entry at that path when the
fetch completed. But the fetch functions themselves rekey entries to a
different (more generic) path upon fulfillment. The deferred upsert
could then move the entry back to the less generic path, undoing the
rekeying.

To fix this, move the upsert logic inline into the fetch functions that
fulfill entries, rather than deferring it to an external callback. This
removes the race condition, simplifies the model, and reduces
implementation complexity. The previous structure existed to avoid the
rekeying cost when vary params weren't available, but rekeying is
inexpensive and not worth the added indirection.

The upsert function itself already handles concurrent writes by
comparing fetch strategies and checking whether the new entry provides
more complete data than any existing entry. So it's safe to always call
it inline — whichever entry wins will be the most complete one.
2026-03-17 13:57:45 -04:00
Andrew Clark d50bef07ca Buffer prefetch response before passing to Flight client (#91487)
**Current:**

1. https://github.com/vercel/next.js/pull/91487

**Up next:**

2. https://github.com/vercel/next.js/pull/91488
3. https://github.com/vercel/next.js/pull/89297

---

Prefetch responses include metadata (in the Flight stream sense, not
HTML document metadata) that describes properties of the overall
response — things like the stale time and the set of params that were
accessed during rendering. Conceptually these are like late HTTP
headers: information that's only known once the response is complete.
Since we can't rely on actual HTTP late headers being supported
everywhere, we encode this metadata in the body of the Flight response.

The mechanism works by including an unresolved thenable in the Flight
payload, then resolving it just before closing the stream. On the
client, after the stream is fully received, we unwrap the thenable
synchronously. This synchronous unwrap relies on the assumption that the
server resolved the thenable before closing the stream.

The server already buffers prefetch responses before sending them, so
the resolved thenable data is always present in the response. However,
HTTP chunking in the browser layer can introduce taskiness when
processing the response, which could prevent Flight from decoding the
full payload synchronously. The existing code includes fallback behavior
for this case (e.g. treating the vary params as unknown), so this
doesn't fix a semantic issue — it strengthens the guarantee so that the
fallback path is never reached.

To do this, we buffer the full response on the client and concatenate it
into a single chunk before passing it to Flight. A single chunk is
necessary because Flight's `processBinaryChunk` processes all rows
synchronously within one call. Multiple chunks would not be sufficient
even if pre-enqueued: the `await` continuation from
`createFromReadableStream` can interleave between chunks, causing
promise value rows to be processed after the root model initializes,
which leaves thenables in a pending state. Since the server already
buffers these responses and they complete during a prefetch (not during
a navigation), this is not a performance consideration.

Full (dynamic) prefetches are not affected by this change. These are
streaming responses — even though they are cached, they are a special
case where dynamic data is treated as if it were cached. They don't need
to be buffered on either the server or the client the way normal cached
responses are.
2026-03-17 13:55:41 -04:00
Hendrik Liebau a46ff7c861 Error when accessing undefined root params in generateStaticParams (#91519)
Follow-up to #91189 which added support for accessing root params inside
`generateStaticParams`. Previously, if a `generateStaticParams` function
tried to read a root param that hasn't been defined yet — either because
the current segment is supposed to define it, or because a child segment
defines it — `getRootParam` would silently return `undefined`. This is
especially problematic because #91019 will type root param getters as
`Promise<string>` without `undefined`, making this a silent type safety
violation.

This change makes `getRootParam` throw an explicit error with the
`generate-static-params` work unit store when the requested param is not
present in `rootParams`.
2026-03-17 17:27:50 +00:00
Niklas Mischkulnig 892f7df054 Turbopack: treat unknown CSS selectors as warnings (#91513)
In many cases, these are just modern selectors that aren't supported yet.
2026-03-17 18:10:39 +01:00
Niklas Mischkulnig 512d7e8128 Turbopack: correctly apply effects for issue snapshots (#91511)
`snapshot_issues` emits effects when `UPDATE=1`
2026-03-17 18:02:35 +01:00
Benjamin Woodruff cdec4740ec docs(turbopack-core): Copy "layers" documentation from mdbook to rustdoc (#91350)
This is implemented as a dummy module that includes a markdown file. Clap does something similar in their rustdocs.

I had claude review the content for accuracy. It made a few modifications. I added rustdoc links by hand.

I converted the mermaid diagram to excalidraw and uploaded the image to vercel blob.

Copied from: https://turbopack-rust-docs.vercel.sh/turbopack/layers.html

Rendered:

![Screenshot 2026-03-13 at 17-13-58 turbopack_core _layers - Rust.png](https://app.graphite.com/user-attachments/assets/d237a05e-da03-405a-8f36-e17e0a351b14.png)
2026-03-17 09:46:39 -07:00
xusd320 9b263e9811 perf(turbopack-node): zero-copy Buffer transfer from node to rust (#91376)
Use `Bytes::from_owner(napi::Buffer)` to eliminate the `Buffer →
Vec<u8>` memcpy on the JS→Rust path in the worker pool.

`bytes::Bytes::from_owner` (>=1.9.0) wraps any `T: AsRef<[u8]> + Send +
'static` without copying — the data pointer comes from `T::as_ref()` and
`T` is dropped when the `Bytes` is dropped. Since napi@2's `Buffer`
implements `AsRef<[u8]>`, this gives true zero-copy.

Also unifies the `Operation` trait to use `Bytes` for both `send` and
`recv`.

This also helps reduce memory consumption, without zero-copy, the memory
consumption of the data returned by the loader would be doubled until GC
on the JS side or dropped on the Rust side.
2026-03-17 09:30:54 -07:00
nextjs-bot ad66dbc89b v16.2.0-canary.102 v16.2.0-canary.102 2026-03-17 16:29:40 +00:00
nextjs-bot 41a2ff65d7 Upgrade React from 5e9eedb5-20260312 to c80a0750-20260312 (#91324) 2026-03-17 15:27:22 +00:00
Tobias Koppers 891adaa605 Improve CSS parse error recovery and reporting (#90025)
## What?

This PR improves CSS parsing error handling in Turbopack by making parse errors recoverable instead of fatal.

1. **Extracting CSS parsing logic** into a new `parse_css_stylesheet` function that handles both parsing and CSS module validation in one place
2. **Fixing error recovery** when CSS minification fails — instead of immediately returning an unparsable result, the code now re-parses the original CSS to recover valid styles
3. **Improving error reporting** by renaming variables for clarity (`source` → `issue_source`) and ensuring errors are properly emitted before attempting recovery
4. **Adding comprehensive tests** for CSS parse error scenarios with both snapshot tests and e2e tests

The snapshot test fixture exercises several CSS features to confirm they survive error recovery:
- Basic selectors (`.before`, `.after`) — valid rules are preserved when surrounded by invalid ones
- `::highlight(highlight-test)` — CSS Custom Highlight API pseudo-element
- `scroll-marker-group`, `::scroll-marker`, `::scroll-marker:target-current` — CSS scroll marker pseudo-elements (new spec)
- `oklch()` color values — converted to `#rrggbbaa` + `lab()` fallbacks for broader browser compatibility

## Why?

Previously, when CSS minification encountered an error, the entire stylesheet would be marked as unparsable, losing any valid CSS that could have been recovered. This meant `turbopackIgnoreIssue` could not suppress the errors since the module produced no output regardless.

Now, when lightningcss recovers from parse errors (collecting them as warnings), issues are still emitted but processing continues with the recovered stylesheet. Only truly fatal parse errors (where `StyleSheet::parse` returns `Err`) remain non-recoverable.

The refactoring also consolidates CSS validation logic into a single function, reducing code duplication and making the error handling flow clearer.

## How?

- Created `parse_css_stylesheet()` function that wraps `StyleSheet::parse()` and applies CSS module validation
- Modified error handling in `process_content()` to call the new function and recover from minification errors by re-parsing
- Changed early returns to error emission followed by recovery attempts
- Added test fixtures and e2e tests to verify CSS parse error recovery works correctly
- Added snapshot tests showing expected output when CSS contains parse errors (valid rules are preserved, invalid ones are skipped)
- Skipped CSS parse error recovery e2e tests for webpack (webpack has its own CSS pipeline)

## Test Plan

- Added e2e tests in `css-parse-error-recovery.test.ts` that verify:
  - Pages with CSS parse errors still render when using `turbopackIgnoreIssue` config
  - Parse errors are properly reported in CLI output
- Added snapshot tests showing CSS output with parse errors, including modern CSS features (`::highlight`, `scroll-marker-group`, `oklch()` color conversion)
- Existing CSS tests continue to pass
2026-03-17 15:44:59 +01:00
Jiwon Choi 66bbcb5819 docs: unstable_catchError() (#89847)
Docs for https://github.com/vercel/next.js/pull/89688
2026-03-17 15:09:39 +01:00
Hendrik Liebau 23fa2787dd Fix inconsistent cache life/tags propagation for cache handler hits (#91454)
When a `"use cache"` entry is newly generated during a prerender
(`prerender` or `prerender-runtime`), `collectResult` defers propagation
of cache life and tags to the outer context. This is because the entry
might later be omitted from the final prerender due to short expire or
stale times, and omitted entries should not affect the prerender's cache
life.

However, when a cache handler returns a hit for an existing entry,
`propagateCacheEntryMetadata` was called unconditionally, without the
same deferral logic. This meant that short-lived cache entries retrieved
from the cache handler could propagate their cache life to the prerender
store, even though they would later be omitted from the final render.

This inconsistency is currently not observable because runtime
prefetches use a prospective and final two-store architecture (see
`prospectiveRuntimeServerPrerender` and `finalRuntimeServerPrerender` in
`app-render.tsx`). The cache handler hit propagation corrupts the
prospective store, but the response is produced from the final store,
which reads from the resume data cache with correct stale and expire
checks. Static prerenders have a similar two-phase architecture that
masks the issue. Because of this, there is no test case that can observe
the incorrect behavior, but the fix avoids confusion and prevents the
inconsistency from becoming a real bug if the architecture changes.

This change extracts a `maybePropagateCacheEntryMetadata` function that
encapsulates the conditional propagation logic and is now called from
both the generation path (inside `collectResult`) and the cache handler
hit path. The resume data cache read path continues to call
`propagateCacheEntryMetadata` unconditionally, since it runs in the
final render phase after short-lived entries have already been filtered
out.
2026-03-17 13:59:11 +00:00
Jiwon Choi 66478522a8 Improve RSC error messages (#90702)
Improve the RSC error messages to be more comprehensive.

x-ref:
https://github.com/vercel/next.js/pull/89688#discussion_r2865847895

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-17 14:37:26 +01:00
Andrew Clark a060fbf9f7 Deflake per-page dynamic stale time test (#91492)
## Summary

- Fixes flaky test "per-page value overrides global staleTimes.dynamic
regardless of direction" introduced by #91437
- The test was flaky because `browser.back()` restored accordion state
from BFCache, causing previously-opened `LinkAccordion` links to be
immediately visible. This triggered uncontrolled re-prefetches outside
the `act` scope. When the `IntersectionObserver` fired inside a
subsequent `act` scope (after clock advancement), stale data would
trigger a prefetch that violated the `no-requests` assertion.
- Fix: instead of navigating back to a previously visited page, navigate
forward to fresh "hub" pages with their own `LinkAccordion` components.
Since these are never-visited pages, accordions start closed and no
uncontrolled prefetches are triggered.
- General principle: when using the router `act` test utility, always
use `LinkAccordion` to control when prefetches happen. Prefetches should
only be triggered inside an `act` scope by toggling an accordion, never
by navigating back to a page where links are already visible.

## Test plan

- [x] Ran the full test file 4 times locally — all 5 tests pass
consistently
2026-03-17 09:14:19 -04:00
Hendrik Liebau 8283b1260b Support accessing root params in "use cache" functions (#91191)
Root params (e.g. `import { lang } from 'next/root-params'`) can now be read inside `"use cache"` functions. The read root param values are automatically included in the cache key so that different root param combinations produce separate cache entries.

Since which root params a cache function reads is only known after execution, the cache key is reconciled post-generation. When root params are read, a two-key scheme is used: the full entry is stored under a specific key (coarse key + root param suffix), and a lightweight redirect entry is stored under the coarse key. The redirect entry's tags encode the root param names using the pattern `_N_RP_<rootParamName>` (e.g. `_N_RP_lang`), following the convention of existing internal tags like `_N_T_<pathname>` (e.g. `_N_T_/dashboard`) for implicit route tags. This allows a cold server to resolve the specific key on the first request after restart. An in-memory map (`knownRootParamsByFunctionId`) provides a fast path for subsequent invocations. When no root params are read, the full entry is stored directly under the coarse key with no redirect involved.

The in-memory map grows monotonically — if a function conditionally reads different root params across invocations, the set accumulates all observed param names. The redirect entry's tags are built from this combined set, ensuring cold servers always resolve the most complete specific key.

The two-key scheme only applies to the cache handler. The Resume Data Cache (RDC) always uses the coarse key because each page gets its own isolated RDC instance, so root params are fixed within a single RDC and no disambiguation is needed. When an RDC entry is found during resume, it seeds `knownRootParamsByFunctionId` so that subsequent cache handler lookups can use the specific key directly.

Reading root params inside `unstable_cache` still throws. Reading root params inside `"use cache"` nested within `unstable_cache` throws with a specific error message explaining the limitation.

Alternatives considered: extending the `CacheEntry` interface (would be a breaking change for custom cache handlers), encoding root param metadata in the stream via a wrapper object, or sentinel byte, or Flightception (runtime overhead of stream manipulation on every cache read), and deferring `cacheHandler.set` until after generation (breaks the cache handler's pending-set deduplication for concurrent requests).
2026-03-17 14:04:44 +01:00