Previously, MPA page loads (reload, anchor navigation) while the instant
navigation lock was held would skip hydration entirely and hard-reload
when the lock released. This meant the page was non-interactive during
the locked period and tests couldn't assert on the hydrated React tree.
Now the page actually hydrates from the static shell. The server injects
a script that fetches the static RSC payload, and the client uses
createFromFetch with allowPartialStream so React can hydrate without
erroring on the incomplete Suspense boundaries. When the lock is
released, a soft refresh fetches the dynamic data (falling back to hard
reload if the router hasn't initialized yet).
Also refactors the cookie protocol to encode the lock state as a JSON
array (`[0]` = pending, `[1, null]` = captured MPA, `[1, {}]` = captured
SPA) for better integration with the devtools UI. This is somewhat
entangled with the hydration changes so they're grouped together, but
can be reviewed as atomic commits.
We've had a longstanding issue where you'd have to run `pnpm i` _twice_ on a fresh checkout because `pnpm` wouldn't set up binary symlinks until after the binary exists, but the binary wouldn't exist until after `pnpm build` was done:
https://github.com/vercel/next.js/blob/canary/.github/workflows/build_reusable.yml#L303-L311
This solves that problem using a pnpm preinstall script that creates stub binaries for these files if they don't exist yet (because we haven't run `pnpm build`).
## Test Plan
```
git clean -fxd
pnpm i
pnpm build
pnpm test-start test/e2e/app-dir/next-image/
pnpm test-start-turbo test/e2e/app-dir/next-image/
```
I tested this both locally and inside of a fresh ubuntu VM.
Running `pnpm next` after `pnpm i` but before `pnpm build` gives a helpful error message:
<img width="946" height="125" alt="Screenshot 2026-03-06 at 9 37 14 PM" src="https://github.com/user-attachments/assets/4517e8bd-60ca-44f2-a34c-f7f3ec56c634" />
The `lint` job in CI has been hitting `FATAL ERROR: Ineffective
mark-compacts near heap limit Allocation failed - JavaScript heap out of
memory` during `lint-eslint`.
`lint-no-typescript` runs `prettier-check`, `lint-eslint .`, and
`lint-language` in parallel via `run-p`. The `lint-eslint` script uses
`eslint.cli.config.mjs` which enables type-checked rules with
`parserOptions.project: true` — the config comment itself notes this
"needs a lot of memory". Node's default heap limit caps around ~2-4GB
regardless of available system memory, which isn't enough when the
type-aware parser loads the full monorepo project graph.
This sets `NODE_OPTIONS="--max-old-space-size=8192"` on the
`lint-no-typescript` script via `cross-env`. The env var inherits into
all three `run-p` children, giving eslint the headroom it needs without
touching CI workflow files.
Fixtures now live next to the code they test, like e2e. `pnpm eval
<name>` packs the local `next` build, generates baseline + agents-md
experiment configs on the fly, and runs both in a sandbox. The agents-md
variant drops an `AGENTS.md` that points the agent at the bundled docs
in `node_modules/next/dist/docs/` — comparing the two variants tells you
whether shipping a doc actually changes agent behavior.
`run-evals.js` mirrors `run-tests.js`: pack once, pass the tarball path
to the child via `NEXT_EVAL_TARBALL` env, forward flags. We only pack
`next`, not the whole workspace — the sandbox is remote Linux, so a
local `@next/swc` darwin binary wouldn't run there anyway; the sandbox
downloads the right one at runtime. The experiment config uses `sandbox:
'auto'`, which picks Vercel sandboxes when credentials are present and
falls back to local Docker otherwise, so external contributors can run
the same evals with just Docker + `ANTHROPIC_API_KEY`.
`experiments/` is generated fresh each run and gitignored so we don't
maintain N committed config files that differ by one line. Fixture code
is excluded from eslint since it's deliberately imperfect code for
agents to fix, and `EVAL.ts` uses vitest rather than jest. Fixture
`package.json` files use `"next": "^16"` rather than a pinned canary so
agents reading `package.json` to infer capabilities aren't misled by a
stale version string; the tarball install overlays it regardless.
next-evals-oss stays as the full benchmark runner for nextjs.org/evals;
it'll pull fixtures from here instead of keeping its own copy.
### What?
Adds a lint script (`scripts/check-unused-turbo-tasks.mjs`) that detects
unused `#[turbo_tasks::function]`, `#[turbo_tasks::value]`, and
`#[turbo_tasks::value_trait]` definitions across the Rust codebase, and
removes all currently unused items.
### Why?
Dead turbo-tasks definitions add compilation overhead (each generates
trait impls, registrations, etc.) and make the codebase harder to
navigate. A lint check prevents regressions.
### How?
**New script** (`scripts/check-unused-turbo-tasks.mjs`):
1. Discovers all `.rs` files in `turbopack/crates/` and `crates/`
(excluding `turbo-tasks-macros-tests`)
2. Parses three kinds of turbo-tasks annotations:
- `#[turbo_tasks::function]` → extracts `fn` name + context (free,
inherent impl, trait impl, trait default)
- `#[turbo_tasks::value]` → extracts `struct`/`enum` name
- `#[turbo_tasks::value_trait]` → extracts `trait` name
3. Scans all files for identifier references, excluding definition sites
4. Reports any name that has no external references
The script is added to the `pnpm lint` command (runs in parallel with
other checks).
**Removed 23 unused items** across 18 files:
| Kind | Count | Examples |
|------|-------|---------|
| `value` (struct/enum) | 9 | `OutputAssetsWithAvailability`,
`ModuleNameMap`, `OptionModuleIds`, `LockedVersions`,
`AssetContentSnapshot` |
| `function` | 14 | `client_env`, `next_js_file`, `convert_path`,
`is_hot_module_replacement_enabled`,
`any_content_changed_of_output_asset` |
Also cleaned up imports that became unused after the removals.
#89819 has been causing a lot of pain for me, because I often have already compiled a version of Turbopack, usually with a custom profile or set of flags, and then I want to just compile the JS bits, but there's no way for me to do it without also recompiling Turbopack again, because the `maybe-build-native` doesn't understand my custom-built binary.
This reverts the behavior of `pnpm build`, but adds a `pnpm build-all` command for people who want this automatic compilation behavior (e.g. agents?).
<!-- 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
## 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 #
-->
This should reduce some duplicate CI jobs.
This split existed to support separate dev/prod manifests for skipping tests. That's not needed anymore, so we can just switch to the Webpack setup. This manual partitioning of `integration` tests into dev vs prod was brittle anyway
We still need this setup for rspack though
Just plumbing and enabling that flag in the experimental shards. The flag will enable our Layout router to use Fragment refs for scroll restoration. Implementation will be done in a follow-up.
### What?
Fix deprecated warning: "Use Object.assign instead of deprecated
util._extend" from http-proxy (which hasn't been updated for 5 years)
### Why?
### How?
Fixes#74460
In favour of https://github.com/vercel/next.js/pull/81544 (which didn't
use a patch, but manually updated the compiled version)