17955 Commits

Author SHA1 Message Date
next-js-bot[bot] d0ac8828c2 v16.3.2 2026-08-21 09:07:01 +00:00
Tim Neutkens f4dc7c82ab [backport] Fix catch-all index page being served for every other slug (#97416)
Backports [Fix catch-all index page being served for every other
slug](https://github.com/vercel/next.js/pull/96553) to `next-16-3`.

<!-- NEXT_JS_LLM -->

Co-authored-by: Andrew Clark <git@andrewclark.io>
2026-08-15 16:23:36 +02:00
next-js-bot[bot] 3d32eb870f v16.3.1 2026-08-13 22:06:37 +00:00
Jude Gao 2b4b1ec111 [backport] Revert i18n localization change for dynamic Pages API routes (#94905) (#97330)
Backports #97327 ("Revert i18n localization change for dynamic Pages API
routes (#94905)") to `next-16-3`.

Clean cherry-pick of `2f84a1f` — `next-16-3` carries the exact code
being reverted, so it applies as-is.

Restores i18n locale prefixing for dynamic Pages API routes, so the
emitted matcher and the adapter's i18n rules agree again on whether
`/api/...` carries a locale prefix. When they disagree the matcher is
unreachable and requests fall through to `/{locale}/404`.

Pairs with the adapter revert (nextjs/adapter-vercel#101, published as
`adapter-vercel@0.0.1-beta.26`). Full rationale and reproduction in
#97327.
2026-08-13 22:01:38 +00:00
Luke Sandberg 228df5fd74 [backport] Retain fewer stale cache versions and use a TTL, plus the mtime fallback (#97304)
Backports the following to `next-16-3`, as a pair:

- #96941 — "[turbopack] Retain fewer stale cache versions and use a TTL"
(`ae1e53a`)
- #97268 — "Add the mtime fallback back" (`52a3410`)

The second fixes a bug in the first: #96941 changes the `CURRENT` file
format from a bare
big-endian `u32` to a JSON object, and an existing cache directory in
the old format could no
longer be aged. #97268 adds the mtime fallback so an unparsable
`CURRENT` is aged by its mtime
instead of failing. They only make sense together, hence one branch.

Both cherry-picked with `-x`, no conflicts. Every touched source file is
byte-identical to canary.

### Verification on this branch

- `cargo check`, `cargo clippy --all-targets`, `cargo fmt --check` on
`turbo-persistence` and `turbo-tasks-backend` — clean
- `cargo test -p turbo-persistence -p turbo-tasks-backend` — all pass, 0
failures
- includes the `db_versioning` tests, notably
`test_unparsable_current_falls_back_to_mtime::case_1_old_u32_format`
(the old-format compatibility case #97268 restores)
- `cargo metadata --locked` — `Cargo.lock` consistent with the
manifests, no lockfile update needed
- `pnpm --filter=next types`, prettier, eslint — clean
- `pnpm build-all` — 18/18 tasks
- `test/production/next-server-nft` — 5/5 tests, 5/5 snapshots pass
(covers the traced `turbopack-cache-seed.js`)

### End-to-end verification against a real old-format cache

`create-next-app@16.3.0` + `next build` (with
`turbopackFileSystemCacheForBuild`) on released
`next@16.3.0` writes a `CURRENT` in the old pre-JSON format — a bare
big-endian `u32`
(`0000 0008`), 30M cache. This branch was then applied with
`pnpm pack-next -p <app> -- --release` (release binary confirmed to
contain
`TURBO_ENGINE_VERSION_TTL_DAYS` and the new `CURRENT` field names), and
two arms were run from
an identical starting state — same cache, same code, only the env var
differing:

| Arm | Build | Cache dirs after |
| --- | --- | --- |
| `TURBO_ENGINE_VERSION_TTL_DAYS=0` | exit 0 | old dir **deleted**, 1
remains |
| default TTL (3 days) | exit 0 | old dir **retained**, intact at 30M, 2
remain |

The retention arm is the meaningful control: without #97268's fallback,
`read_current_version`
would error on the bare-`u32` file, the age would become
`Duration::MAX`, and the directory would
be evicted in *both* arms. Retention under the default TTL is direct
evidence the mtime fallback
works.

Also confirmed:

- new `CURRENT` is written as JSON:
`{"max_sequence_number":8,"commit_time":"..."}`
- retention cap holds: seeded 4 version dirs → build left exactly 2
(current + one most-recently-used other)
- clean deletion, no `DELETION_PREFIX` leftovers in any run
- `TURBO_ENGINE_VERSION_TTL_DAYS=notanumber` warns (`expected a whole
number of days`) and falls back to the 3-day default rather than failing

<!-- NEXT_JS_LLM -->
2026-08-13 21:43:26 +00:00
Andrew Clark 16eab3ac5d [backport] Fix: Optimistic routing bugs leading to repeated prefetch loops (#97325)
Backports #97128 ("Fix: Optimistic routing bugs leading to repeated
prefetch loops") to `next-16-3`.

Fixes an infinite prefetch loop in optimistic routing. A proxy that
rewrites every URL to inject a leading path segment — i18n with the
default locale hidden from the URL, so `/one/two` is served as
`/en/one/two` — combined with a fully dynamic target route like
`/[locale]/[...pages]`, produces a prefetch that never settles. When a
prefetch response came back, the client never checked whether the
rendered tree matched the one it had requested. When the params baked
into the request were wrong because of the rewrite, the task kept
re-attempting to fulfill the missing data and looped forever.

The prefetch path now records a dynamic-rewrite marker on the local
route definition when the response's tree diverges from the requested
one, which disables further optimistic resolution of that route and
invalidates the entries derived from it. This is the same strategy
normal navigation responses already used, now applied to the prefetch
path.

A second, related bug is also fixed: parallel routes with conflicting
dynamic params at the same level (`@modal/[...catchAll]` next to
`[username]`) can't be distinguished by the traversal algorithm, which
assumes each segment resolves independently without inspecting sibling
branches. When that scenario is detected, optimistic routing is now
disabled for the conflicting route rather than binding one branch's URL
parts to another branch's params.

### Verification on this branch

Cherry-picked from `5942b37a42`. Three source files conflicted, all
because `next-16-3` predates the post-16.3.0 refactor that split the
client's response decoder into its own module:

- `segment-cache/decode-server-response.ts` — this file does not exist
on this branch; it was extracted out of `navigation.ts` after 16.3.0.
Its change (tracking whether the decoded tree's segment identities
diverge from the base request tree) was ported into `navigation.ts`,
where the equivalent logic still lives. Canary decodes a compact
transport tree and compares segment identities inline during the walk;
this branch instead merges the response patch onto the base
`FlightRouterState` before converting it to a `RouteTree`. The check is
therefore re-expressed as a read-only walk in
`convertServerPatchToFullTree` that compares each server patch against
the base tree it is applied to, following canary's descent
(`convertServerPatchToFullTreeImpl`) exactly and using the same
comparison rules: segment identity via `matchSegment`, page segments
matched modulo embedded search params, server-filled `__DEFAULT__`
segments ignored, and inactive parallel-route branches (those carrying a
refresh state in the base) skipped. The result is threaded out through
`NavigationSeed.treeDivergedFromBase`.
- `segment-cache/cache.ts` — the consuming block in
`fetchSegmentPrefetchesUsingDynamicRequest` (mark-as-dynamic-rewrite,
invalidate derived entries, reject with immediate expiry) is
byte-identical to canary. The surrounding conflicts were only
incidental: the `RouteTreeAccumulator` type gaining
`treeDivergedFromBase`, and canary's transport-decoder call vs. this
branch's `convertRootFlightRouterStateToRouteTree`. Resolved by keeping
this branch's calls and adding the new field to the accumulators.
Canary's inline `NavigationSeed` construction in
`writeDynamicTreeResponseIntoCache` does not apply here because that
function delegates to `convertServerPatchToFullTree`, which now carries
the field.
- `router-reducer/create-initial-router-state.ts` — a one-line
accumulator initializer; kept this branch's
`convertRootFlightRouterStateToRouteTree` call and added
`treeDivergedFromBase: false`.

The other touched source files applied cleanly and are byte-identical to
canary: `segment-cache/optimistic-routes.ts` (the parallel-route
conflict detection) and `route-params.ts` (exporting
`canonicalizeURLPart`), along with `ppr-navigations.ts`, the small
cleanly-applied hunk in `navigation.ts`, and every test fixture.

`tsc` passes on the `next` package for all files touched here.
Behavioral verification is left to CI on this branch; the change carries
the `proxy-prefix-rewrite-prefetch-loop` and
`modal-catchall-sibling-dynamic-prefetch-loop` end-to-end suites plus
additions to the `optimistic-routing` suite.

Opened as a draft because the `decode-server-response.ts` →
`navigation.ts` port is a manual re-implementation across the refactor
boundary rather than a mechanical cherry-pick. The divergence walk in
`convertServerPatchToFullTree` is the part most worth a close review.

<!-- NEXT_JS_LLM -->
2026-08-13 17:17:46 -04:00
Andrew Clark 75548c9a2b [backport] Fix Nav Inspector request loop on repeat captures (#97326)
Backports #97050 ("Fix Nav Inspector request loop on repeat captures")
to `next-16-3`.

With the Nav Inspector enabled, clicking a `<Link prefetch={true}>`,
closing the inspector, navigating home, re-enabling it, and clicking the
same link left the app hung in a pending "Compiling…" state while it
fired prefetch requests in an infinite loop (~30/sec). The Instant
Navigation Testing lock restricted navigation reads to entries created
within the current lock scope (`ownedEntries`), enforced as a post-hoc
filter after the cache lookup. Because the segment cache resolves
lookups by most-specific-match, a previous scope's runtime-prefetch
entries at concrete param keypaths kept winning the lookup, the filter
kept rejecting them, and the locked prefetch created its replacement at
a more generic keypath that could never win — so every scheduler pass
discarded and refetched forever.

Rather than patch the filter, the fix replaces the `ownedEntries`
mechanism with a per-scope segment `CacheMap`. Each lock scope owns a
private map that starts empty and is discarded at release, so a captured
navigation structurally observes only data fetched under the lock. The
map is bound to a unit of work when it is created: a prefetch task
captures its map when scheduled (`PrefetchTask.segmentCacheMap`, the
single place that consults lock state), a locked navigation inherits its
driving task's map, and everything else — unlocked navigations,
hydration, refreshes, history-traversal restores, server actions and
patches — binds to the shared map. Reads and response writes receive the
map explicitly, so a request that straddles a scope boundary still
writes into the map its entries live in.

In production builds without the testing API this compiles down to the
previous single-map behavior, so the non-testing paths are unchanged.

### Verification on this branch

Cherry-picked from `5dc3ae1fe7`. Three files conflicted; each was
resolved by keeping the release branch's existing code shape and
applying only the fix's actual change — threading the segment `CacheMap`
through as an explicit parameter.

- **`refresh-reducer.ts`** and **`restore-reducer.ts`** — the conflicts
were confined to the import block. Canary imports
`convertServerPatchToFullTree` from
`./segment-cache/decode-server-response`, a module that doesn't exist on
this branch; `next-16-3` still imports it from
`./segment-cache/navigation`. I kept the release branch's import source
and added the new `segmentCacheMap` import alongside it. The
function-body changes (passing `segmentCacheMap` as the shared map)
applied cleanly, and the `startPPRNavigation`/`spawnDynamicRequests`
argument order lines up with this branch's signatures.

- **`cache.ts`** — the substantive one. `next-16-3` predates the
post-16.3.0 segment-cache refactors, so three areas differ from canary
and had to be adapted rather than taken verbatim from the patch: (1)
`RouteTree` is still non-generic here (`RouteTree`, not
`RouteTree<RSCSegmentData | null>` — this branch has no `RSCSegmentData`
and the tree doesn't carry render data), so the three signatures the
patch retyped were kept as plain `RouteTree`; (2)
`writeSegmentBundleResponse` still has the older `StaticShell`/`else`
re-key split guarded by `__NEXT_VARY_PARAMS`, versus canary's single
`upsertSegmentEntry` — I kept the release-branch logic and threaded
`map` into both of its `upsertSegmentEntry` calls; (3)
`writeDynamicRenderResponseIntoCache` and `writeSeedDataIntoCache` still
use the `flightDatas` + `CacheNodeSeedData` write model rather than
canary's route-tree-carries-data model — I kept the release-branch
traversal and threaded `map` into each `writeSeedDataIntoCache` /
`fulfillEntrySpawnedByRuntimePrefetch` call. Everything else in
`cache.ts` — the `ownedEntries` removal, the `segmentCacheMap` export,
`insertEmptySegmentCacheEntry`, and the `map` parameter added to every
read/write helper — applied cleanly and is identical to canary. I
confirmed no stale references to the removed `ownedEntries` /
`readSegmentCacheEntry` / `navigationLockPrefetch`-param APIs remain,
and that every call site of the retyped helpers passes a map.

The remaining files applied without conflict. `server-patch-reducer.ts`,
`navigation-testing-lock.ts`, `navigation-testing-lock.disabled.ts`, and
the `instant-navs-devtools` test are byte-identical to canary.
`create-initial-router-state.ts`, `ppr-navigations.ts`,
`server-action-reducer.ts`, `navigation.ts`, and `scheduler.ts` differ
from canary only in pre-existing `next-16-3` divergence (the older
transport-data / route-tree models); the fix's own hunks (the
`segmentCacheMap` threading) applied to them cleanly. No local typecheck
was run — this fresh worktree has no `node_modules` and installing it
carries the known corepack/SWC-binary gotchas — so behavioral and type
verification is left to CI on this branch, which carries the
`instant-navs-devtools` regression test from #96692.

**Reviewer, focus here:** the `writeSegmentBundleResponse` resolution in
`cache.ts` (area 2 above) is the hunk I'd most like a second pair of
eyes on. I preserved this branch's `StaticShell`/`else` re-key branch
structure and only inserted the `map` argument into its two
`upsertSegmentEntry` calls; please confirm that routing both branches'
writes through the task's bound map (rather than the module-level
`segmentCacheMap`) is the correct behavior for the older re-key logic.

<!-- NEXT_JS_LLM -->

Co-authored-by: Sam Selikoff <sam.selikoff@gmail.com>
2026-08-13 17:17:27 -04:00
Hendrik Liebau 9dbb357dbe [backport] Encode the cache item name built by unstable_cache (#97313)
Backports #96936 ("[refactor] Rename `encodeCacheTag` to
`encodeHeaderSafe`") and #96937 ("Encode the cache item name built by
`unstable_cache`") to `next-16-3`.

Fixes #76286. A cache implementation may serialize cache metadata into
HTTP request headers, whose values are limited to Latin-1.
`unstable_cache` assembles a cache item name from the request URL and
the name of the cached callback, and neither part was encoded. When that
name holds a character above U+00FF the conversion throws before the
request is dispatched, so the read never reaches the cache and the write
that follows it fails the same way. Nothing is stored, nothing is found,
and the entry falls back to the origin on every render.

The reachable case is a non-ASCII query parameter on any dynamic route
that calls `unstable_cache`, whether or not the route reads
`searchParams`, because `URLSearchParams` returns decoded keys and
values while the pathname stays percent-encoded.

The rename is folded in because it is the prerequisite: #96937 imports
`encodeHeaderSafe` from `server/lib/encode-header-safe`, which the
rename creates. It carries no behavior change, the function body is
identical apart from the parameter name, and the two were stacked in
this same order on canary.

### Verification on this branch

Cherry-picked from `1f0cd93b83` and `4286a42e2c` in that order, with no
conflicts.

`packages/next/src/server/web/spec-extension/unstable-cache.ts` is not
byte-identical to canary, because the release branch predates the
unrelated `willConsumerServerCache` change and keeps
`workStore.isStaticGeneration` at those two call sites. The diff this
backport applies to that file is exactly the encoding change and nothing
else. Every other source file is byte-identical to canary, and no
reference to the old `encodeCacheTag` name remains.

Behavioral verification is left to CI on this branch; the change carries
the `non-ascii-cache-item-name` end-to-end suite, which exercises a real
cache handler.

<!-- NEXT_JS_LLM -->
2026-08-13 15:59:11 -04:00
Hendrik Liebau 323a17c1b3 [backport] Discard only cache entries that predate a tag revalidation, and reuse completed entries (#97314)
Backports #96726 ("Discard only cache entries that predate a tag
revalidation") and #96727 ("Reuse completed cache entries for the rest
of a request") to `next-16-3`.

Calling `updateTag()` in a server action made every later read of a
cache carrying that tag regenerate for the remainder of the request,
including reads of an entry that had just been generated after the
invalidation and therefore already reflected it. Two sequential reads of
the same cache function during the re-render produced two different
values within a single render, and each one repeated the work.
`isRecentlyRevalidatedTag` only asked whether a tag appeared in
`pendingRevalidatedTags`, and that array lives for the whole
`WorkStore`, which spans a server action and the render that follows it.
Each pending revalidated tag now records a `revalidatedAt` timestamp
taken from the same clock as `CacheEntry.timestamp`, and the renamed
`isRevalidatedAfter` reports an entry as stale only when the
revalidation is newer than the entry.

The second change fixes a `'use cache: private'` function executing its
body twice when it is called twice in one request, which defeats the
preload-then-read shape that motivates a preload in the first place. The
intra-request dedupe map dropped an entry as soon as its fill completed,
so it only ever covered concurrent invocations, and private caches have
no cache handler in production to fall back to. Completed invocations
now move into `completedCacheInvocations` on the work store instead of
being dropped.

The two are backported together because #96727 builds on the discard
checks that #96726 restructures. They were stacked in this order on
canary, and #96727 conflicts in
`test/e2e/app-dir/use-cache/use-cache.test.ts` when applied alone.

### Verification on this branch

Cherry-picked from `5da1c1ae03` and `1ff6a80358` in that order, with no
conflicts. All source files are byte-identical to canary. Behavioral
verification is left to CI on this branch; the changes carry the
`action-dedupe`, `dedup-sequential` and `private-dedup-sequential`
fixtures in the `use-cache` suite, and a handler-call count assertion in
`use-cache-custom-handler`.

<!-- NEXT_JS_LLM -->
2026-08-13 19:58:49 +00:00
Hendrik Liebau 6d67f46a4d [backport] Keep the dev validation worker alive across HMR updates (#97315)
Backports #96988 ("Keep the dev validation worker alive across HMR
updates") to `next-16-3`.

Cache Components dev validation reported stack frames that pointed at
build output whenever a module had been updated while the dev server
ran. The overlay showed a raw `file:` URL, the terminal named the chunk
rather than the page, and because the frame never resolved to a source
position there was no code frame either, so nothing indicated which line
caused the error. This affected both the static shell validation and the
instant-navigation validation, since both run on the same worker.

Turbopack's server HMR evaluates an updated module as a script of its
own, carrying its source map inline rather than on disk, so only the
isolate that ran that `eval` can resolve a frame in it. The validation
worker never ran it. The worker now replays the applied updates, the
cleared manifest cache entries, and the evicted paths that the dev
server reports, in the same order the dev server applied them, which
leaves each updated module's inline source map in the worker's own
Node.js cache.

Not dropping the worker on every edit also removes a worker respawn and
a `loadComponents` call from each validation that follows an edit, which
is paid at exactly the moment the user is waiting for the insight. The
case in the test suite that covers this went from around 870ms to around
240ms.

This affects Turbopack dev with Cache Components, which is on by default
unless `experimental.devValidationWorker` is set to `false`.

### Verification on this branch

Cherry-picked from `f70564f742` with no conflicts. All source files are
byte-identical to canary. Behavioral verification is left to CI on this
branch; the change carries the `dev-validation-worker-source-maps`
suite, whose snapshots turn from a frame naming the chunk into resolved
frames.

<!-- NEXT_JS_LLM -->
2026-08-13 15:58:43 -04:00
Hendrik Liebau c19a64dcff [backport] Restore the live headers() view of the incoming request (#97311)
Backports #97166 ("Restore the live `headers()` view of the incoming
request") to `next-16-3`.

Fixes #97049. This is a regression introduced in 16.3.0 itself. #94703
and #95116 are both in the `v16.3.0` tag, and between them they left
`headers()` detached from the request it describes. A Proxy that writes
a header onto `request.headers` and then reads it back through
`headers()` gets the value from before the write, while reading the same
header directly off `request.headers` returns the new value, so two APIs
describing the same request disagree. Whether the write is seen at all
depends on ordering, because the view is built on first access.

`HeadersAdapter.seal` now accepts a set of header names to omit from
every read operation, and `getHeaders` seals the shared request headers
directly instead of stripping them from a copy. Both earlier intentions
survive: the internal headers stay on the request where the framework
still reads them, they stay invisible to userland `headers()`, and the
sealed view tracks the request again.

### Verification on this branch

Cherry-picked from `3c97df56ea` with no conflicts. All source files are
byte-identical to canary. Behavioral verification is left to CI on this
branch; the change carries unit coverage in `headers.test.ts` and the
`proxy-headers-live-view` end-to-end suite.

<!-- NEXT_JS_LLM -->

Co-authored-by: joaquin <tachsinachmet@gmail.com>
2026-08-13 15:54:32 -04:00
Luke Sandberg d2aa1b63d2 [backport] Fix missing styled-jsx styles in Pages Router SSR on adapter builds (#97302)
Backports #96632 ("Fix missing styled-jsx styles in Pages Router SSR on
adapter builds") to `next-16-3`.

Cherry-picked from 2049354a66 with no
conflicts.

### Verification on this branch

- `cargo check -p next-api`, `cargo clippy -p next-api`, `cargo fmt
--check -p next-api` — clean
- `pnpm --filter=next types` — clean
- `test/production/adapter-styled-jsx` — passes with both Turbopack and
webpack
- `test/production/adapter-root`, `adapter-config`,
`adapter-config-i18n` — pass (no regression from the `trace_endpoint`
signature change)

<!-- NEXT_JS_LLM -->

Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
Co-authored-by: vercel-fleet[bot] <308483924+vercel-fleet[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-08-13 12:05:30 -07:00
Niklas Mischkulnig ec2b319781 [backport] Bump @swc/helpers (#96885)
Backport https://github.com/vercel/next.js/pull/96720
2026-08-07 13:31:53 -07:00
Steven 3f07d5eabc [16.x] fix(next/image): preserve image response after optimization (#96733)
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Steven <229881+styfle@users.noreply.github.com>
2026-08-06 07:49:00 +02:00
next-js-bot[bot] d73f5622e2 v16.3.0 2026-08-03 20:06:44 +00:00
Andrew Clark 8e878d4848 Remove implicit Partial Prefetching opt-in from instant (#96539)
Previously, adding a truthy `instant` config to a route segment (e.g.
`export const instant = true`) did two things: it enabled instant
navigation validation, and it implicitly opted the route into Partial
Prefetching.

Now those concerns are fully separate. `instant` only controls
validation, so enabling it no longer silently changes how a route is
prefetched. Partial Prefetching is enabled with the `prefetch` segment
config (`'partial'` or `'unstable_eager'`), or globally with the
`partialPrefetching` option in next.config.

For routes whose only opt-in was a truthy `instant`, `<Link
prefetch={true}>` goes back to performing a legacy full prefetch instead
of being downgraded to a partial one. `instant = false` still opts a
route out of the dev-time `prefetch={true}` warning.

Adds a regression test asserting that a route that only exports `instant
= true` keeps the full-prefetch behavior.
2026-08-03 19:56:47 +00:00
Aurora Scharff 8a4920c15a docs: clarify first-party Skills workflows (#96495)
## Summary

- clarify how runtime foundations, interactive workflows, and unattended
loops fit into the AI agents and Instant Navigation guides
- harden the Cache Components and Partial Prefetching Skills around
capability checks, trustworthy `instant()` tests, route scope, and
real-app verification failures
- add migration-guide comments to temporary Partial Prefetching route
config and update `remove-partial-prefetch` to remove only generated
comments while preserving runtime-prefetch TODOs and user comments

This keeps the experimental Partial Prefetching optimizer in #96471
separate.

## Testing

- `pnpm --filter @next/codemod build`
- `pnpm --filter @next/codemod test --
transforms/__tests__/remove-partial-prefetch.test.js --runInBand`
2026-08-03 19:23:56 +00:00
Joseph 459617a125 fix: double fragment on navigation (#93132)
Fixes: https://github.com/vercel/next.js/issues/93126,
https://github.com/vercel/next.js/issues/95551

Repro:

- from `/` navigate to `/abc#foo`
- on `/abc#foo` - now click `/abc#bar`
- the url is now at `/abc#foo#bar`

Unlike, https://github.com/vercel/next.js/pull/93855, this PR fixes what
gets stored in the segment cache navigation, rather than slicing away an
existing hash. It is plausible that dropping the hash from the segment
cache is not desired though.
2026-08-03 18:21:06 +02:00
Tim Neutkens cbf0cef687 Enable TypeScript CLI by default (#96497)
## Summary

Enable the project-local TypeScript CLI checker by default during `next
build`, while preserving `experimental.useTypeScriptCli: false` as an
opt-out to the TypeScript compiler API. Update the TypeScript 7
guidance, diagnostics, tests, and documentation to match the new
default.

## Verification

- `pnpm exec jest --runTestsByPath test/unit/isolated/config.test.ts`
- Pre-commit lint-staged checks passed (Prettier and ESLint)
- Not run: `pnpm test-start-turbo
test/production/app-dir/typescript-cli/typescript-cli.test.ts` (isolated
fixture dependency installation was blocked by unavailable npm registry
access)
- Not run: `pnpm --filter=next types` (repository has pre-existing
unrelated TypeScript errors)

<!-- NEXT_JS_LLM -->
2026-08-03 18:10:51 +02:00
David Alexandru Ilie da90782311 Fix App Router scroll padding visibility (#96308)
## Summary

- Treat the root `scroll-padding-top` as the lower boundary of the
usable viewport during App Router navigations, so content obscured by a
sticky header is not incorrectly considered visible.
- Preserve the merged Fragment scroll-ownership state machine: empty
Fragments remain unavailable targets, while real targets are classified
against the padding-aware viewport.
- Resolve pixel and percentage values and use the same visibility rule
in both the legacy element handler and the Fragment-ref handler.
- Add regression coverage showing that Next scrolls when the destination
is hidden inside the padding boundary, while preserving the current
scroll position when the destination is genuinely visible below it.

## Performance

The root computed style is resolved lazily, only after the candidate
produces client rects. Empty Fragments and hash navigations do not
perform this lookup. For a real route-scroll target, the resolved value
is cached locally and reused by the second geometry check after
`scrollTop = 0`.

This does not add work to scroll events or every render, and keeping the
value local allows responsive CSS, root classes, and custom properties
to change between navigations. In a 50-iteration dirty-style Chromium
probe, both the existing and updated paths reported 50 style
recalculations and 50 layouts, indicating that the lookup reused the
style/layout update already required by the handler in that test.

## Related work

#96342 is now merged into `canary` and handles empty Fragment scroll
ownership. This PR composes with its `NoClientRects` / `InViewport` /
`OutOfViewport` state machine by changing only the visible-region
boundary for real targets.

## Verification

- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/router-autoscroll/router-autoscroll.test.ts -t "scroll
padding"` (3/3)
- `HEADLESS=true __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=false pnpm
test-dev-turbo
test/e2e/app-dir/router-autoscroll/router-autoscroll.test.ts -t "scroll
padding"` (3/3)
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/parallel-routes-scroll-owner/parallel-routes-scroll-owner.test.ts`
(8/8)
- `HEADLESS=true pnpm test-dev-webpack
test/e2e/app-dir/parallel-routes-scroll-owner/parallel-routes-scroll-owner.test.ts`
(8/8)
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/navigation-focus/navigation-focus.test.ts` (5/5)
- Manually compared the same sticky-header reproduction before and after
the change.

<!-- NEXT_JS_LLM -->
2026-08-03 16:00:14 +01:00
Janka Uryga 331739243f [Cache] Make caches error if called after prerender aborts (#96426)
Closes #96339

Caches that started filling after a prerender was aborted would produce
an empty stream, because an aborted `renderSignal` made [the signal we
pass to `prerender()` inside `use-cache-wrapper`] be aborted
immediately. So we produced an empty entry and saved it, essentially
poisoning the cache.

Fixed by removing `renderSignal` from the `AbortSignal.any(...)` we pass
in and returning a rejected promise before we get to the cache filling
codepath. This should not be observable to userspace because the
prerender is already aborted.
2026-08-03 11:42:26 +00:00
next-js-bot[bot] 4fd843fb48 v16.3.0-canary.107 2026-08-02 23:34:28 +00:00
Tim Neutkens 3d4d46f1c9 Enable Turbopack build filesystem cache by default (#96493)
## Summary

Enable `experimental.turbopackFileSystemCacheForBuild` by default in all
environments, including generic CI. Explicitly setting the option to
`false` remains the opt-out. Update the focused coverage and
documentation to match.

## Verification

- `IS_TURBOPACK_TEST=1 pnpm test-start-turbo
test/e2e/filesystem-cache/build-cache-default.test.ts`
- `pnpm prettier --with-node-modules --ignore-path .prettierignore
--check packages/next/src/server/config-shared.ts
test/e2e/filesystem-cache/build-cache-default.test.ts
docs/01-app/03-api-reference/08-turbopack.mdx
docs/01-app/03-api-reference/05-config/01-next-config-js/turbopackFileSystemCache.mdx`
- Not run: `pnpm --filter=next build` (the checkout has 11 pre-existing
TypeScript errors in unrelated files)

<!-- NEXT_JS_LLM -->
2026-08-02 11:33:33 -07:00
next-js-bot[bot] 9480f7f9ff v16.3.0-canary.106 2026-08-01 23:33:55 +00:00
Hendrik Liebau ccd47cfe53 Warn that experimental.useCache is deprecated (#96448) 2026-07-31 20:26:10 -04:00
Zack Tanner 893121b887 Fix hybrid Pages/App Router not-found rendering with adapters (#96392)
## Summary

When Pages Router and App Router routes coexist, a Pages Router route
that returns `notFound` will internally hand rendering to the App Router
not-found page. Next.js [prefers the App Router `/_not-found` entry for
404
responses](https://github.com/vercel/next.js/blob/5f5e4773bbb54f19697063ea093e8d89ca7d081a/packages/next/src/server/base-server.ts#L2965-L2980)
before falling back to a Pages Router `/404` page.

When deployed via an adapter, this means it would have attempted to
resume the Pages entry, which wouldn't contain the relevant prerendered
shell or postponed state. If the not-found page suspends, its response
cannot be resumed using the metadata associated with the Pages Router
output.

For adapter requests, this marks the handoff with an empty postponed
state. This uses the existing resume protocol to perform a complete,
non-cacheable dynamic render. `next start` continues to use the normal
PPR path, where it can load and resume the App Router not-found output
directly.

This trades PPR shell reuse for correctness only during this
cross-router not-found handoff. Longer term, adapters should have an
explicit contract for cross-output handoffs so they can select and
resume the App Router not-found output normally, without necessarily
declaring the pages router output PPR-capable.

The change also documents the empty postponed-state contract and adds
deployment regression coverage.

## Verification

- Added an end-to-end test that verifies a Pages Router `notFound`
response includes the dynamic App Router not-found content when
deployed.
- `pnpm testheadless test/e2e/app-dir/pages-router-app-not-found`
- `pnpm --filter=next types`

<!-- NEXT_JS_LLM -->
2026-07-31 16:40:37 -07:00
next-js-bot[bot] a8dcd2562f v16.3.0-canary.105 2026-07-31 23:35:12 +00:00
Sam Poder fd6f11baf3 [turbopack] Introduce documentation on experimental.turbopackChunking (#96437)
Pulled out of https://github.com/vercel/next.js/pull/96398, wanted to
edit this a bit before pushing.
2026-07-31 14:21:34 -07:00
Vercel Release Bot f5c81a82c1 Upgrade React from 0f42eac2-20260730 to cbb046ab-20260731 (#96434)
Co-authored-by: next-js-bot[bot] <279046576+next-js-bot[bot]@users.noreply.github.com>
2026-07-31 15:27:34 -04:00
Tobias Koppers 6523a33f78 Enable turbopackFileSystemCacheForBuild by default (#96395)
### What?

Makes `experimental.turbopackFileSystemCacheForBuild` (Turbopack's
on-disk
filesystem cache for `next build`) enabled by default, matching the
dev-mode
flag. It stays disabled by default in non-Vercel CI environments, and
can
always be turned off explicitly with
`experimental.turbopackFileSystemCacheForBuild: false`.

### Why?

The build filesystem cache greatly speeds up warm builds, but was
previously
opt-in (only auto-enabled for canary builds on the Vercel builder).
Defaulting
it on gives everyone faster subsequent builds, while keeping it off in
non-Vercel CI where the cache is unlikely to persist between builds.

### How?

- `turbopackFileSystemCacheForBuildDefault()` now returns `true` except
when
  `isCI && !NOW_BUILDER` (removed the `isStableBuild()` gate).
- The explicit `false` opt-out already works via the existing
  `config.experimental?.turbopackFileSystemCacheForBuild || false` read.
- Adds an e2e test (`test/e2e/app-dir/turbopack-fs-cache-build-default`)
asserting `.next/cache/turbopack` is written by default, empty when the
flag
  is `false`, and empty in non-Vercel CI.
- Updates the `turbopackFileSystemCache` / Turbopack docs.

<!-- NEXT_JS_LLM -->

---------

Co-authored-by: vercel-fleet[bot] <308483924+vercel-fleet[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-07-31 13:24:40 -04:00
Sebastian "Sebbie" Silbermann 7612eaeda1 Consolidate unhandled rejection logging into a single listener (#95999)
An unhandled rejection was previously logged by up to three independent
process listeners at once: the render runtime's crash-prevention handler
in `process-error-handlers.ts` (a bare `console.error`), the router
server's `Log.error('unhandledRejection: ', err)`, and the dev server's
`logErrorWithOriginalStack`. The runtime handler must exist on every
deployment target (#77997), but on self-hosted `next start`/`next dev`
it shares a process with the router server's and dev server's listeners,
so a single rejection was logged multiple times in different formats.

THe first commits adds a test showing the current behavior where we log
multiple times.

The second commit introduces `registerUnhandledRejectionListener` and
`isUnhandledRejectionListenerRegistered` in `process-error-handlers.ts`,
and converts the router server and dev server to check-then-register
instead of installing their own rejection loggers:

- The listener function is shared via a `Symbol.for` key on
`globalThis`, so multiple copies of the module (e.g. in the pre-compiled
server bundle and in a route module bundle) register and detect a single
listener instance.
- The registration check queries
`process.listeners('unhandledRejection')` instead of a module-global
flag, so it stays accurate even after external code calls
`process.removeAllListeners`. `installProcessErrorHandlers` therefore
calls the register function unconditionally.

The `uncaughtException` handlers are left as they are; they have the
same duplication and could be consolidated the same way in a follow-up.
2026-07-31 11:36:40 -04:00
Zack Tanner 9bfaf63edb Fix adapter outputs for not-found routes when used with cache components (#96390)
## Summary

Fix the build output for App Router `not-found` routes that get
partially prerendered when cacheComponents is enabled.

Previously, Next.js treated any statically generated `/_not-found` route
as a complete static 404, even when it contained postponed state. It
copied the partial HTML shell to `server/pages/404.html` and omitted the
corresponding `/_not-found` prerender from adapter outputs.

This change distinguishes fully static not-found routes from partially
prerendered ones. Fully static routes retain the existing `404.html`
behavior, while PPR routes remain resumable prerender outputs.

This allows a `not-found.tsx` that suspends under Cache Components to
render its dynamic content when deployed.

Closes https://github.com/vercel/next.js/pull/96399

## Testing

Expanded `not-found-non-document-dynamic` coverage to verify that:

- dynamic not-found content renders for ordinary missing paths
- dynamic not-found content renders when `/_not-found` is selected by a
rewrite
- a PPR not-found does not emit `server/pages/404.html`
- its adapter output includes `PARTIALLY_STATIC` metadata, postponed
state, and a resume chain

Also verified that fully static App Router not-found routes continue to
emit and serve `pages/404.html`.

- `__NEXT_CACHE_COMPONENTS=true pnpm test-start-turbo
test/e2e/app-dir/not-found-non-document-dynamic/not-found-non-document-dynamic.test.ts`
- `pnpm test-start-turbo test/e2e/app-dir/not-found/basic/index.test.ts`
2026-07-31 08:30:57 -07:00
Sam Poder b4e3fecc79 [turbopack] add experimental.turbopackChunking config (#96398)
Built using @sokra's Fleet! This re-arranges all of our chunking
experimental features under `experimental.turbopackChunking` and adds
the following options:

```javascript
/**
 * Avoid creating more than one chunk smaller than this size, in bytes. Smaller
 * chunks are merged into bigger ones to avoid that. Defaults to `50000` (50 KB).
 */
minChunkSize?: number
/**
 * Avoid creating more than this number of chunks per chunk group. Chunks are
 * merged into bigger ones to avoid that. Defaults to `40`.
 */
maxChunkCountPerGroup?: number
/**
 * Never merge chunks bigger than this size, in bytes, with other chunks. This keeps code
 * in big chunks from being duplicated across multiple chunks. Defaults to `200000` (200 KB).
 */
maxMergeChunkSize?: number
/**
 * Minimum size, in bytes, for a component chunk to be emitted on its own when
 * `generateComponentChunks` is enabled. Component chunks smaller than this are folded into a
 * single remainder chunk. Defaults to `20000` (20 KB).
 */
minComponentChunkSize?: number
```
2026-07-30 23:37:37 -07:00
Andrew Clark e3d634e0bc Fix isHeadPartial when hydrating from a static fallback shell (#96400)
When a static fallback shell is served as-is — e.g. from a CDN in a
deployed environment — the client can't hydrate from inline Flight data;
it fetches the full RSC payload separately and reconstructs the initial
payload on the client. During that reconstruction, the head's partiality
flag was accidentally set to the head itself. Since the head is always
truthy, the head was unconditionally treated as partial.

This happens to be unobservable today: the only flows that reach the
reconstruction are fallback-shell prerenders, where the server
pessimistically marks the head partial anyway, and when Cache Components
is enabled the client ignores the per-payload flag entirely in favor of
response-level partiality. But we're about to rework the RSC response
format so that head partiality has a single, load-bearing source of
truth, so the flag needs to survive the reconstruction correctly rather
than by coincidence.

While auditing the payload consumers, I also fixed the payload shape
check in segment prefetch generation (it used `&&` where `||` was
intended, so it accepted any shape) and deleted
`should-hard-navigate.ts`, which no longer has any callers on the
client.

Adds e2e coverage asserting that metadata survives hydrating a fallback
shell page: the title is correct after hydration, the head is included
in prefetched data, and a return navigation is served entirely from the
cache that was populated during initial hydration. In deployed
environments this exercises the client resume path, which is where the
reconstruction runs.
2026-07-31 00:37:15 -04:00
next-js-bot[bot] 38f0cdee46 v16.3.0-canary.104 2026-07-30 23:34:58 +00:00
Zack Tanner bfb0d3c1b9 Remove obsolete shouldWaitOnAllReady render option (#96401)
## Summary

`shouldWaitOnAllReady` previously allowed an otherwise dynamic App
Router render to wait for every Suspense boundary before sending the
response. After [#96367](https://github.com/vercel/next.js/pull/96367),
no caller enables this behavior anymore.

This removes the unused option and makes `supportsDynamicResponse` the
sole signal for whether App Router generates static HTML. Static
generation still waits for `allReady`, while dynamic responses continue
streaming.

<!-- NEXT_JS_LLM -->
2026-07-30 14:52:48 -07:00
Vercel Release Bot a49f1c97e6 Upgrade React from 6cb4322d-20260729 to 0f42eac2-20260730 (#96402)
Co-authored-by: next-js-bot[bot] <279046576+next-js-bot[bot]@users.noreply.github.com>
2026-07-30 17:21:07 -04:00
Zack Tanner 0b3ce7b8fe fix: respect htmlLimitedBots in cache components without buffering the full response (#96367)
## Summary

DOM-capable crawlers can consume streamed metadata, but with
cacheComponents enabled, it treated them like HTML-limited bots and
forced a full dynamic render. This meant capable crawlers could not use
the prerendered shell even when they were not matched by
`htmlLimitedBots`.

This change lets DOM-capable crawlers follow the normal PPR shell and
streaming-metadata path. `htmlLimitedBots` remains the source of truth
for requests that need blocking metadata, including when a custom
pattern explicitly matches a DOM-capable crawler.

## Test Plan
Tests were added that contained a Suspense boundary that intentionally
never resolves. The test reads the response stream until it receives the
fallback, then verifies that blocking metadata has already been emitted
while the unresolved page content has not. A fully buffered response
would never return, so this avoids relying on timing or arbitrary
delays.

This PR is stacked on #96366.
2026-07-30 13:30:41 -07:00
Steven 8ca5321d08 fix(create-next-app): make AGENTS.md blue (#95432)
I noticed that this last question for AGENTS.md doesn't have any blue
highlighted word.

So I made AGENTS.md blue to follow the convention of the others.

<img width="865" height="163" alt="image"
src="https://github.com/user-attachments/assets/d10f74e1-f5ec-4c8a-9c6d-8d01b9c1abf3"
/>

---------

Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
2026-07-30 15:54:22 -04:00
Vercel Release Bot 5f5e4773bb Upgrade React from 1724e9ce-20260729 to 6cb4322d-20260729 (#96389) 2026-07-30 17:49:17 +00:00
David Alexandru Ilie e689163afb Fix empty Fragment scroll ownership (#96342)
## Summary

- treat Fragment instances without client rects as unavailable route
scroll targets
- leave the shared scroll signal unconsumed so another changed segment
can handle an empty route Fragment
- avoid focus and scroll side effects when an empty segment cannot claim
ownership
- consume and clear an explicit hash intent when no matching target
exists, without falling back to route content
- cover empty and visible intercepted routes across both scroll
handlers, including focus preservation and real hash scrolling

This PR is stacked on #96323, which provides the minimal reproduction.

## Verification

- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/parallel-routes-scroll-owner/parallel-routes-scroll-owner.test.ts`
(8/8)
- `HEADLESS=true pnpm test-dev-webpack
test/e2e/app-dir/parallel-routes-scroll-owner/parallel-routes-scroll-owner.test.ts`
(8/8)
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/navigation-focus/navigation-focus.test.ts` (5/5)

<!-- NEXT_JS_LLM -->
2026-07-30 18:37:14 +01:00
Vercel Release Bot 769d9e6e03 Upgrade React from 96fcba90-20260728 to 1724e9ce-20260729 (#96355)
Co-authored-by: next-js-bot[bot] <279046576+next-js-bot[bot]@users.noreply.github.com>
2026-07-30 13:51:10 +00:00
Janka Uryga 8c609c3ecb [Instant] Point to first blocking await in validation (#96343)
Closes NAR-854

### TLDR

New approach to #94949. We can work around the problem of debug info
getting clamped when re-serializing by doing more work, which should be
good enough unless we can figure out the correct way to adjust the
clamping behavior in react.

### The problem

Before this PR, instant validation works like this:
1. deserialize the full page payload (in stages)
2. reserialize each segment (in stages)
3. reassemble the segments into a combined payload, varying the stage
per segment as needed

Unfortunately, in step 2, we lose timing debug info that tells us when
IO operations started/ended:

https://github.com/facebook/react/blob/c235eb40f1c3121798d233bf992fd8f8c8558d06/packages/react-server/src/ReactFlightServer.js#L5550-L5553

This means that even if we provide an appropriate `endTime` during step
3, no IO info will be omitted by `filterDebugInfo`, because it was all
re-timed to when the render occurred. In practice, this means that when
we report a blocking dynamic hole, it might point to a different await
than the one that was actually blocking in the stage that we rendered
the segment in.

### The solution

(h/t to @gnoff for coming up with this trick)

We can work around this problem with no changes in React. We just have
to apply an `endTime` when deserializing the payload for the first time,
before reserializing each segments. However, this means that we can no
longer disassemble the payload into segments in one pass -- e.g. if we
trim the debug info to what it'd be in the `Static` stage, we can't get
good data for the `Runtime` stage anymore. So we have to run the segment
disassembly separately for each stage we care about.

The debugInfo can now vary depending on the stage, we need to change the
shape of `SegmentCacheItem`. Previously, it was like this:
```
'/foo':
  chunks:
    Static: [...]
    Runtime: [...]
    Dynamic: [...]
  debugChunks: [...]
```
But to account for the varying debug info, we now need to store the
partial chunks, the complete chunks, and the debug chunks separately for
each stage:
```
'/foo':
  Static:
    chunks: [...]
    allChunks: [...]
    debugChunks: [...]
  Runtime:
    chunks: [...]
    allChunks: [...]
    debugChunks: [...]
  Dynamic:
    ...
```
Most of the changes in the PR come from adjusting the code to
- create separate entries for each segmentPath + stage in the new way
outlined above, using an `endTime` based on the stage
- consume the cache entry appropriately
2026-07-30 12:05:28 +02:00
next-js-bot[bot] f3edea1b06 v16.3.0-canary.103 2026-07-29 23:35:42 +00:00
Joseph b59fae0429 [fragment-scroll] Stop blurring on navigations (#96113)
Dynamic pages (all pages in dev), blur and select the body as
`activeElement`.

- [x] e2e for https://github.com/vercel/next.js/issues/96050
- [ ] attempt a fix that prevents blur
2026-07-29 19:13:35 -04:00
Zack Tanner 67f16a522d Fix PPR rendering for configured HTML bots (#96364)
## Summary

When PPR is enabled, requests matching a custom `htmlLimitedBots`
pattern could still use the prerendered route path. As a result, the
configured blocking-metadata behavior was not applied consistently.

Use the existing streaming-metadata decision when selecting the PPR
render path. Built-in bots retain their existing fully buffered
behavior, custom HTML-limited bots can continue streaming body content
after metadata resolves, and regular user agents remain unchanged.

## Verification

- HEADLESS=true pnpm test-start-turbo
test/e2e/app-dir/html-limited-bots-ppr/html-limited-bots-ppr.test.ts
- HEADLESS=true pnpm test-start-webpack
test/e2e/app-dir/html-limited-bots-ppr/html-limited-bots-ppr.test.ts

Co-authored-by: Sebastian Silbermann <sebastian.silbermann@vercel.com>
2026-07-29 15:49:57 -07:00
Joseph 5cdcbbb05b Bump postcss to 8.5.23 (#96107)
Note that https://github.com/advisories/GHSA-6g55-p6wh-862q does not
affect Next.js users unless they build from untrusted source code (which
would have more severe security implications). We're merging this to
reduce noise from security scanners.

- 8.5.21 is 50h+ old so it can be installed
- 8.5.22 is still under 48h - valid at 2026-07-24 08:48:15 UTC 

Closes: https://github.com/vercel/next.js/issues/96349
2026-07-29 18:22:56 -04:00
Sebastian "Sebbie" Silbermann 4286e5ff35 Ensure unique resolved headers() value between render passes (#96085)
A HTTP request can render the same React tree in multiple passes with
different semantics for `connection()`: the prospective and final
prerenders of a runtime prefetch, or a navigation's dynamic render and
the runtime prerender that is spawned from it to refresh the client's
prefetch cache. All of these passes previously resolved `headers()` to
the single sealed headers object created lazily by the request store, so
userland caches keyed on the identity of `await headers()` (any
per-request memoization that treats the headers object as "the request")
leaked promises across passes.

With this change, every render pass resolves `headers()` to a distinct
object over the same underlying data, making "the headers object
identifies the request within one render pass" a real contract.


---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 18:16:34 -04:00
Andrew Clark 3717cd3dfb Gate partial fallback shell upgrades behind partialPrefetching for next start (#96297)
In #96074 I put the `partialFallback` behavior behind the
`partialPrefetching` flag, so that we don't risk an explosion in ISR
costs for apps that haven't opted into Partial Prefetching. But it turns
out I only handled this for deploy mode — i.e. the build output consumed
by the Vercel adapter. The behavior was still unconditionally on for
`next start`.

The reason it was easy to miss is that `next start` and the adapter
express partial fallback shells through completely separate mechanisms.
The adapter emits a `partialFallback` flag into the build output and
lets the platform's ISR layer perform the shell upgrade. `next start`
has no such flag; the server performs the upgrade itself, in the
compiled page runtime. So the gate I added to the adapter output had no
effect on the self-hosted path.

To understand how the `next start` path was configured, I looked at the
PR that removed the original top-level `partialFallbacks` config flag
(#93859). Before that PR, the behavior was gated in the runtime; when
the flag was removed those gates were deleted and the behavior became
always-on.

The important subtlety is that only the *upgrade* should be gated, not
the shell machinery as a whole. On `next start`, the value that decides
whether a shell can be specialized (`remainingPrerenderableParams`) also
drives core Cache Components behavior — build-time partial prerendering
of params and serving build-time sub-shells — which must stay on
regardless of `partialPrefetching`. So this gates only the two things
that make up the "upgrade on first request" cost risk: the background
ISR revalidation that specializes a shell per request, and the
client-facing `isFallbackUpgradeable` signal that tells the client to
retry a prefetch waiting for that upgrade.

Adds coverage with a `partialPrefetching`-disabled fixture asserting
that fallback shells stay shared rather than specializing per request.
2026-07-29 19:56:49 +00:00
Jimmy Miller 39db24a48b Added cache copying support for worktrees (#95646)
This lets next detect if it is in a worktree and find the main repo and
copy turbopack caches for build or dev to make initial build faster. I
wasn't sure about commiting tests for this and how exactly we'd won't to
go about this with the git requirement. I also could do some unit tests,
but following the library conventions there wasn't a nice abstracted out
file system. So I did various tests locally. If anyone has good ideas on
what kind of testing totally opened to it.

Also left a comment that I don't look in other worktrees but instead the
root project. Not sure if this is the right answer or not. But works for
the patterns I think people typically follow.
2026-07-29 14:52:35 -04:00