Commit Graph
34947 Commits
Author SHA1 Message Date
Sam PoderandGitHub 625dccbc4a Revert "[turbopack] Track re-exports in import_usage inside of compute_import_usage" (#96311)
Reverts vercel/next.js#95989
2026-07-28 20:29:13 +00:00
Zack TannerandGitHub ccfe67aa8d Return 200 for client-handled Server Action redirects (#96310)
## Summary

Client-handled Server Action redirects currently return a `303` without
a `Location` header and may include a streamed RSC response body. Some
HTTP intermediaries reject this response or strip its body because it
looks like an HTTP redirect.

Return `200` for client-handled Server Action redirects while continuing
to use `x-action-redirect` and the RSC response. Progressive-enhancement
form submissions still return a standards-compliant `303` with
`Location`.

Fixes #92882
Fixes #74026

## Verification

- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/actions/app-action.test.ts
test/e2e/app-dir/app-basepath/index.test.ts
test/e2e/app-dir/actions/app-action-progressive-enhancement.test.ts`
- `HEADLESS=true pnpm test-start-turbo
test/e2e/app-dir/actions/app-action.test.ts
test/e2e/app-dir/app-basepath/index.test.ts
test/e2e/app-dir/actions/app-action-progressive-enhancement.test.ts`
- `pnpm --filter=next types`

<!-- NEXT_JS_LLM -->
2026-07-28 16:04:34 -04:00
Aurora ScharffandGitHub a28e346a1a docs(interactive-apps): update prefetching for unified runtime prefetching (#96302)
### What?

Update the interactive apps guide's prefetching section for unified
runtime prefetching.

### Why?

#96106 removed the `allow-runtime` segment config, and the guide's
section still described the old model after the mechanical rename: it
told readers session data (cookies, headers) needs the link opt-in, when
the App Shell already carries session-gated UI. Only URL data needs
`prefetch={true}`.

### How?

Rework the section around the per-link model: the shell carries static,
cached, and session output, `prefetch={true}` resolves URL data ahead of
the click, and the static prefetch attempt (#96095) makes the server
cost an internal optimization. Includes a technical-writing pass on the
section (full-noun subjects, split splices) and aligns the Next steps
table row.
2026-07-28 15:52:51 -04:00
JosephandGitHub 7ec73949fc docs: clarify instrumentation-client polyfill loading and hydration timing (#95988)
The `instrumentation-client` Polyfills docs recommended a conditional
dynamic `import().then()` to load a polyfill. That import is
fire-and-forget: Next.js loads this file synchronously and does not
await it before hydration, so the polyfill can install after components
have already run (see #83905).

- In "Execution timing" mention that only synchronous top-level code
completes before hydration. Call out that a conditional `import()` here
is not awaited, and cross-reference `supported-browsers` for
load-on-demand strategies and the built-in baseline.

- In **`supported-browsers.mdx`**, "fix" the `Conditionally loading
polyfills` example to `await import()` instead of a floating `.then()`,
otherwise, a reader needs to put their code within the `.then` chain of
the polyfill load.

Closes:
https://linear.app/vercel/issue/DOC-6506/revisit-polyfills-with-instrumentation-client
2026-07-28 15:44:03 -04:00
Sebastian "Sebbie" SilbermannandGitHub 1412d0fe91 [cd] Send Slack notification if publish fails (#96304)
Failing to publish (especially a partial publish) leaves Next.js in a
corrupted state that needs to be addressed immediately. However, we only
created alerts when deploy tests failed which requires a successful
publish.

Now we send special message to the same channel as broken canary and
deploy tests go.

This won't catch a failing deploy-target job. We'll see if deploy-target
also frequently fails/flakes.
2026-07-28 15:21:21 -04:00
Sebastian "Sebbie" SilbermannandGitHub 6ab0dd6fcd [ci] Install Node.js from the official tarball in the native-builder image (#96298)
Fixes e.g.
https://github.com/vercel/next.js/actions/runs/30314671467/job/90137708228#step:12:603

The `native-builder` image (`scripts/native-builder.Dockerfile`), used
by the `build-native` job of `build_and_deploy.yml` to cross-compile
`next-swc`, installed Node.js 20 via the NodeSource setup script. That
script downloads and imports a signing key before configuring the apt
repository. When the key download returns an HTTP 403 from CI runners
the script still exits successfully, so `apt-get install nodejs` falls
back to Ubuntu's stock `nodejs` package, which does **not** bundle
`npm`. The failure only surfaced two layers later when the image ran
`npm i -g @napi-rs/cli` (`npm: not found`, exit 127), breaking the
native build.

This change installs Node.js 20 from the official `nodejs.org` static
tarball instead. The tarball bundles `npm` and `corepack`, is
glibc-linked (matching the previous intent), and does not depend on
NodeSource's apt repository or key server, removing the flaky external
dependency. The install step also asserts `npm --version`, so a future
regression fails at the install layer rather than several layers later.


## test plan

- [x] [full build_and_deploy on this
branch](https://github.com/vercel/next.js/actions/runs/30372888336)
2026-07-28 15:21:20 -04:00
Vercel Release BotGitHubnext-js-bot[bot] <279046576+next-js-bot[bot]@users.noreply.github.com>
9ce6d48cd4 Upgrade React from 756fdd47-20260727 to 96fcba90-20260728 (#96306)
Co-authored-by: next-js-bot[bot] <279046576+next-js-bot[bot]@users.noreply.github.com>
2026-07-28 15:19:38 -04:00
Zack TannerandGitHub b368a504a8 Fix Turbopack worker URL context lookup (#96307)
## Summary

- Preserve the original static worker request when generating the
Turbopack module-context lookup, instead of passing an already-rewritten
URL object.
- Adds an e2e fixture for bare-module Worker URLs with conditional
wildcard exports.

It looks like this might have regressed in #88602

Fixes #94015
Closes #94043 

## Verification

- `cargo check -p turbopack-ecmascript`
- `HEADLESS=true pnpm test-start-turbo
test/e2e/app-dir/worker-module-url/worker-module-url.test.ts`
- `HEADLESS=true pnpm test-start-webpack
test/e2e/app-dir/worker-module-url/worker-module-url.test.ts`
2026-07-28 11:48:17 -07:00
d61f16eed3 [turbopack] Treat Object.defineProperty(exports, …, { value: … }) as side-effect free (#96273)
Treat code like this:

```
Object.defineProperty(exports, '__esModule', { value: true });
exports.foo = 1;
```

As side-effect free. This is an extension of the work on
https://github.com/vercel/next.js/pull/94293 and will allow
https://github.com/vercel/next.js/pull/95718 to drop more dead requires.

---------

Co-authored-by: Benjamin Woodruff <[email protected]>
2026-07-28 11:40:29 -07:00
David Alexandru IlieandGitHub 925edc3dd6 Classify Instant Insights as internal in Request Insights (#96147)
## Summary

- Classifies Instant Insights records as internal and hides them by
default.
- Adds a persisted “Internal activity” toggle.
- Nests internal records below their matching foreground request.
- Preserves orphaned internal records exactly once.
- Signals hidden internal errors without littering the main request
list.
- Raw endpoint and CLI data remain complete; filtering is UI-only.

## Verification

```bash
pnpm build-all
pnpm --filter=next types
pnpm --filter=next build
pnpm test-unit --runTestsByPath packages/next/src/next-devtools/dev-overlay/components/request-insights/trace-viewer.test.ts
pnpm test-dev-turbo test/development/app-dir/request-insights/request-insights.test.ts
pnpm test-dev-webpack test/development/app-dir/request-insights/request-insights.test.ts
```

<!-- NEXT_JS_LLM -->
2026-07-28 14:15:21 -04:00
David Alexandru IlieandGitHub c294cb7539 Persist Request Insights display settings (#96277)
## Summary

- Replaces the per-request verbose checkbox with a global Request
Insights settings menu.
- Persists `requestInsights.verbose` through the existing DevTools
configuration path.
- Restores the setting across reloads.
- Does not include the unrelated global `pagehide` flush behavior.

## Verification

```bash
pnpm build-all
pnpm --filter=next types
pnpm --filter=next build
pnpm test-dev-turbo test/development/app-dir/request-insights/request-insights.test.ts
pnpm test-dev-webpack test/development/app-dir/request-insights/request-insights.test.ts
```

<!-- NEXT_JS_LLM -->
2026-07-28 14:15:21 -04:00
efcbb27450 Add Instant Insights pipeline spans (#95961)
## Summary

- Adds a detached `Instant Insights` root span.
- Surfaces `Prepare validation inputs` and `Run validation` as child
phases.
- Covers both the validation worker and Webpack’s in-process path.
- Stores these spans separately from the foreground request.
- Preserves scheduling, cancellation, and concurrent-document behavior.

## Verification

```bash
pnpm build-all
pnpm --filter=next types
pnpm --filter=next build
pnpm test-unit --runTestsByPath packages/next/src/next-devtools/dev-overlay/components/request-insights/trace-viewer.test.ts
pnpm test-dev-turbo test/development/app-dir/request-insights/request-insights.test.ts
pnpm test-dev-webpack test/development/app-dir/request-insights/request-insights.test.ts
pnpm test-dev-turbo test/development/app-dir/instant-validation-scheduling/instant-validation-scheduling.test.ts
```

<!-- NEXT_JS_LLM -->

---------

Co-authored-by: David Ilie <[email protected]>
2026-07-28 14:15:21 -04:00
David Alexandru IlieandGitHub 7716976400 Isolate local Request Insights spans from OpenTelemetry (#96276)
## Summary

- Keeps Request Insights diagnostic spans inside the local recorder.
- Nested generic Next.js spans retain the local parent hierarchy.
- The user’s active OpenTelemetry context remains active and unchanged.
- Diagnostic wrappers are not delegated to or exported through user
OpenTelemetry.

## Verification

```bash
pnpm build-all
pnpm --filter=next types
pnpm --filter=next build
pnpm test-unit --runTestsByPath packages/next/src/server/lib/trace/local-span-recorder.test.ts packages/next/src/server/lib/trace/tracer.test.ts
```

<!-- NEXT_JS_LLM -->
2026-07-28 14:15:20 -04:00
David Alexandru IlieandGitHub bc856629e7 Add local Request Insights span tracing (#96275)
## Summary

- Adds a local `traceLocalSpan` primitive for Request Insights
diagnostics.
- Supports detached roots and nested local parentage.
- Records thrown errors, marks span status, rethrows, and always ends
the span.
- Keeps this foundational behavior separate from tracer/OTel
integration.

## Verification

```bash
pnpm build-all
pnpm --filter=next types
pnpm --filter=next build
pnpm test-unit --runTestsByPath packages/next/src/server/lib/trace/local-span-recorder.test.ts
```

<!-- NEXT_JS_LLM -->
2026-07-28 14:15:20 -04:00
StevenandGitHub 7c19c7c06a fix(next/image): improve test coverage for unsupported image types (#96301)
We were missing some test coverage for image optimization. This PR adds
tests for many missing image formats.

Additionally, this PR also ensures the sharp encoders match that
allowlist from detectContentType() such that future versions of sharp
that enable new encoders by default are not automatically enabled in
next/image.

Practically, there is no change to the end user because
detectContentType() runs before sharp so that is the true allowlist, but
this gives us a little more control to align the sharp allowlist as well
(since sharp can release new features in semver minor).
2026-07-28 14:05:06 -04:00
JosephandGitHub 6b572345d3 doc: typegen for typedRoutes (#96296)
`typegen` also generated the inputs needed for `typedRoutes`
2026-07-28 13:40:22 -04:00
Tim NeutkensandGitHub c23d1a946c feat(turbopack): support import.meta.env (#96225)
## Summary

- add native Turbopack support for `import.meta.env.DEV`, `PROD`,
`MODE`, `BASE_URL`, and `SSR`
- statically analyze built-in values for dead-code elimination while
exposing the complete runtime object
- derive `BASE_URL` from the Next.js `basePath` and include
Vite-compatible trailing-slash formatting
- add TypeScript declarations, focused execution and Next.js integration
coverage, and Vite migration documentation

## Verification

- `cargo fmt --all -- --check`
- `cargo nextest run -p turbopack-tests -E 'test(import_meta_env)'`
- `pnpm build-all`
- `pnpm --filter=next types`
- `NEXT_TEST_PREFER_OFFLINE=1 pnpm test-dev-turbo
test/e2e/import-meta-env/import-meta-env.test.ts`
- `NEXT_TEST_PREFER_OFFLINE=1 pnpm test-start-turbo
test/e2e/import-meta-env/import-meta-env.test.ts`

<!-- NEXT_JS_LLM -->
2026-07-28 17:34:04 +00:00
Tim NeutkensandGitHub 1de0812714 Add option to control dev memory threshold restarts (#96144)
## Summary

Adds `experimental.devMemoryThresholdRestart`, defaulting to `true`, so
projects can set it to `false` to opt out of the development server
restart that occurs when V8 heap usage exceeds 80%. The option has no
effect on `next start`.

## Verification

- Focused config-loading and memory-threshold unit suites passed
- `pnpm --filter=next build` completed JavaScript compilation, then
failed on pre-existing `sharp` and `@vercel/detect-agent` declaration
errors unrelated to this change

<!-- NEXT_JS_LLM -->
2026-07-28 13:22:22 -04:00
next-js-bot[bot]andGitHub ad618bf13f Restore canary version 16.3.0-canary.99 after v16.3.0-preview.10 preview release 2026-07-28 15:54:36 +00:00
next-js-bot[bot]andGitHub 35917f58d2 v16.3.0-preview.10 v16.3.0-preview.10 2026-07-28 15:54:33 +00:00
Andrew ClarkandGitHub 3de2d1a213 Unify allow-runtime with Partial Prefetching (#96106)
Removes the "allow-runtime" prefetch config, and turns its behavior on
implicitly wherever Partial Prefetching is enabled.

The original motivation for "allow-runtime" was to give apps more
control over server costs triggered by prefetches. Until a route
explicitly opts in, prefetches would only be served from the CDN, not
from the server. The problem, though, was it was very confusing to know
when to add or remove this configuration. The incentive for many apps
was to add it everywhere, with no clear signal for when to remove it.

Our updated thinking is that Partial Prefetching itself already provides
sufficient protection against runaway prefetching costs: per-link
prefetches only happen on Link components that explicitly opt in with
the prefetch prop.

The optimizations landed earlier in this stack also make allow-runtime
less necessary: on pages where all the content is statically renderable,
prefetches are served from the static cache and no runtime request is
ever issued; only a page that accesses non-static data is prefetched at
runtime.

The upshot of this decision is that runtime versus static becomes an
internal optimization; the same content gets prefetched regardless of
whether or how Next.js is able to optimize it.
2026-07-28 11:51:29 -04:00
Andrew ClarkandGitHub dfa7f4f713 Attempt static prefetch before resorting to runtime (#96095)
If we're reasonably confident that a segment can be prefetched
statically without omitting data that would have been included during a
runtime prefetch (e.g. cookies), the client should attempt prefetch the
segment statically instead of going straight to a runtime request.

The decision for whether to do this is based on the
ShouldAttemptStaticPrefetch added in previous steps.

If it turns out the static response is not sufficient, then it will fall
back to a runtime request.

This makes prefetching cheaper for pages that are fully statically
renderable. We can make the optimization better in the future with more
reliable per-segment computation of the ShouldAttemptStaticPrefetch, but
the current approach should at least work for fully static pages, which
is what's most important.

This optimization applies during both the Shell phase and the
Speculative phase of the prefetching algorithm.
2026-07-28 11:51:29 -04:00
next-js-bot[bot]andGitHub e70495c1ee v16.3.0-canary.99 v16.3.0-canary.99 2026-07-28 14:57:37 +00:00
next-js-bot[bot]andGitHub aae4179ac6 v16.3.0-canary.98 v16.3.0-canary.98 2026-07-27 23:36:11 +00:00
Andrew ClarkandGitHub bdbbb1e887 Block prefetch task until sufficient response is received (#96017)
This is a refactor of the prefetching algorithm to block the prefetch
task from completing until the response is received.

Currently, a prefetch task is fire-and-forget: once a prefetch's
requests are spawned, the scheduler marks the originating task as
complete regardless of what the server ends up returning. However,
certain planned optimizations require us to verify the response from the
server to determine whether additional work is necessary. For example,
we may attempt to do a static prefetch instead of a runtime prefetch if
we optimistically assume that the static prefetch does not vary on
runtime data. We need to verify that assumption is correct before
marking the task as complete.

Now, whenever a pass over the task spawns a segment request, or
encounters an entry whose response hasn't arrived yet, the task
registers itself on the entry and exits as Blocked instead of Done. When
the entry resolves, the blocked task is pinged and the pass re-runs
against the received data. Only a pass that observes every response it
cares about advances the task to the next phase or completes it.

Rejected entries are handled per segment: a rejection counts as an
observed response, so the pass skips that segment and keeps prefetching
the rest. The task never registers on a rejected entry, since nothing
ever pings one. Retries are governed by the existing per-entry logic.

Also fixes a cache bug this exposed: when a revalidation response is
keyed at a more generic vary path than the stale partial entry that
prompted it (because the segment turned out not to vary on some param),
the stale entry shadowed the result on every lookup — the upgraded data
was unreachable and the revalidation was wasted. With blocking, that
dead end became an infinite loop: each fulfillment re-pinged the blocked
task, which re-read the same stale entry and spawned another
revalidation. Upserting (or re-keying) a segment now deletes settled
entries at more specific vary paths that the incoming entry supersedes.

The router-act test helper's "at least one request initiated" watchdog
is adjusted to account for the new blocking behavior: countable requests
can now be legitimately gated on an in-flight App Shell response (which
the watchdog intentionally doesn't count). On expiry it now proceeds to
response processing if the act scope intercepted a shell request, and
keeps waiting while shell requests are in flight elsewhere on the page,
instead of timing out.
2026-07-27 19:03:16 -04:00
David Alexandru IlieandGitHub a619237b1c Use a safe clock for Request Insights bookkeeping (#96274)
Draft replacement for #96267 after restructuring the Request Insights
stack.

## Summary

- Request Insights bookkeeping used `Date.now()`, which Cache Components
patches to detect unstable application wall-clock reads.
- Framework bookkeeping could therefore be incorrectly attributed to
user code.
- Implicit timestamps now use `performance.timeOrigin +
performance.now()`.
- Explicit OpenTelemetry timestamp inputs remain unchanged.
- Tests distinguish framework bookkeeping from a genuine application
`Date.now()` call.

## Verification

```bash
pnpm build-all
pnpm --filter=next types
pnpm --filter=next build
pnpm test-unit --runTestsByPath packages/next/src/server/lib/trace/local-span-recorder.test.ts packages/next/src/server/lib/trace/span-store.test.ts packages/next/src/server/lib/trace/request-insights.test.ts
pnpm test-dev-turbo test/development/app-dir/request-insights/request-insights.test.ts
pnpm test-dev-webpack test/development/app-dir/request-insights/request-insights.test.ts
```

<!-- NEXT_JS_LLM -->
2026-07-27 18:58:54 -04:00
8cd4a50f09 Count key length in remaining byte-budgeted LRUs (#96231)
Applies the key accounting from #96229 to the other in-memory LRUs with
byte budgets:

- the incremental memory cache
- the default `use cache` handler
- the dev `staticPathsCache` and `serverComponentsHmrCache`
- the dev source-map URL cache.

Keys were free against each budget in the same way; unlike the
filesystem route cache there are no production leak reports for these,
so this is preventative and kept separate. Count-based caches and the
disk-byte LRU are unchanged.

## Test Plan

Each size callback change adds `cacheKey.length` to whatever it returned
before. The memory cache callback is also restructured from early
returns into if/else chains; each `CachedRouteKind` branch returns the
same value as before, plus the key length.

To see the change do something, run this against the built memory cache:

```js
const { getMemoryCache } = require('next/dist/server/lib/incremental-cache/memory-cache.external')
const cache = getMemoryCache(1024 * 1024) // 1 MB budget
for (let i = 0; i < 20000; i++) {
  cache.set('/api/data?' + 'p'.repeat(1000) + i, {
    value: { kind: 'FETCH', data: { body: 'ok' } },
    lastModified: 1,
  })
}
console.log(cache.size, cache.currentSize)
```

Before this change, all 20,000 entries are admitted: the cache counts
260 KB against its 1 MB budget while the keys alone hold ~20 MB, and it
keeps growing as long as new keys arrive. After, it holds 1,020 entries
and the counted size stops at the budget.

For regressions: `test/production/app-dir/revalidate` and
`test/e2e/app-dir/use-cache-default-handler-expire-zero` pass in prod
mode (these exercise the fetch cache and the default `use cache`
handler), plus the `packages/next/src/server/lib` jest suites and `pnpm
--filter=next types`.

Co-authored-by: Marcos Hernanz <[email protected]>
2026-07-27 23:39:17 +01:00
danandGitHub cdc390babc Serve cached misses from the filesystem route cache (#96230)
`getItem` stores `null` for paths that don't resolve, but checks the
cached value with `if (lruResult)`, so a cached `null` is never served:
every request for an unknown path — including every dynamic-route
request — re-runs the lookup. It's been this way since the cache was
introduced in #52492.

Return the cached entry whenever the key is present. This can't go
stale: in production the item sets are only populated during
`setupFsCheck`, and the LRU is disabled in dev.

Cached misses are stored as a dedicated sentinel instead of `null`.
Since serving a cached miss has no observable effect, no test can catch
this regressing — that is also why it went unnoticed. With the sentinel
it's a type error instead: reintroducing the truthy check fails `pnpm
types` (verified). Same pattern as `invalidSourceMap` in
`source-maps.ts`.

Stacked on #96229, which bounds what negative entries can retain.

## Test Plan

No direct test: the change is unobservable by design, and the existing
e2e suites assert that it stays that way. The regression guard is the
type system, as above. `test/e2e/app-dir/not-found/basic` passes in prod
mode (14/14), exercising the negative-cache path.
2026-07-27 23:39:17 +01:00
9a31a17600 Count URL key length in the filesystem route cache LRU (#96229)
Fixes #94890

Adopted from #96187 and #94699, with both authors credited on the
commit.

The router server keeps an in-memory cache that maps request URL paths
to what they resolve to: a page, a static file, or nothing.

It's bounded by a size budget, but the size of each entry was computed
from the cached value only. The URL string used as the key was free, and
"resolves to nothing" entries counted as size 1. So a long-lived server
receiving many unique URLs — bot crawls, long slugs, `/order/:id`-style
traffic — could accumulate up to ~1M URL strings while the cache
believed it was within budget.

That looks like a slow memory leak that tracks how many distinct URLs
you've served rather than how much traffic you get. #94890 has several
production reports, including two deployments that patched key
accounting in and saw memory go flat.

The idea of the fix is to make the cache's bookkeeping match what an
entry actually keeps alive, so the budget becomes a real memory bound.
That takes three things:

1. Count the key string, not just the value.
2. Count the fixed cost an entry has regardless of string lengths — the
per-entry bookkeeping objects, measured at ~120 bytes. Without this, a
flood of *short* unique URLs stays "cheap" on paper while the
bookkeeping itself grows unbounded.
3. Copy strings before caching them. When V8 takes a substring of a long
string (which is how the pathname is extracted from the request URL), it
doesn't copy the characters — it creates a small object that points into
the original string, and the original can't be garbage-collected while
the substring is alive. So caching a 30-char pathname could actually
hold the entire URL in memory, kilobyte query string and all, and the
bookkeeping only sees the 30 chars. Copying the string before caching
makes it hold only its own characters.

With the bookkeeping honest, the budget is set to 8 MiB, roughly the
real memory the cache could already use for legitimate entries before
this change, so capacity isn't reduced — the previous attempt at this
fix (#94942) stalled on that concern.


## Test Plan

A/B on a production `next start` serving 60k requests with unique long
URLs (the reported traffic shape): canary grows +79 MB and keeps
growing; this branch grows +9 MB and plateaus — the budget working as
intended. The per-entry costs above were measured directly on the cache
with forced GC (they fit `118 + key length` bytes within a few percent,
which is also what predicts the A/B numbers). `lru-cache` unit tests
(26/26) and `pnpm --filter=next types` pass.

---------

Co-authored-by: Marcos Hernanz <[email protected]>
Co-authored-by: dmchoi77 <[email protected]>
2026-07-27 23:39:16 +01:00
Benjamin WoodruffandGitHub 606b8e88bb Turbopack: Only extend the watcher's batch window for unfiltered events (#96186)
If we get metadata events, or if we (on Linux with inotify) get file
access events, these aren't useful, and we filter them, but we were
unintentionally extending the deadline for the batch because of them.
2026-07-27 15:07:04 -07:00
Benjamin WoodruffandGitHub 59160c612d Turbopack: Store watcher batch state with bitflags instead of 4 different sets (#96114)
Previously we we had 5 total `FxHashSet`s in `BatchedInvalidations`. Now
we've got one `FxHashMap` and one `FxHashSet`. There was potential
overlap in paths represented in all of these sets.

The old implementation was suboptimal for two reasons:
- We ended up cloning and storing `PathBuf` more times. (Note: there's a
good future opportunity for an arena here too)
- If a path was in both `path` and `path_and_children`, we'd later try
to invalidate paths associated with that twice. This will be a no-op as
we have a lock on the invalidator map and the duplicated path has
already removed from the invalidator map, but it makes us do extra map
lookups that aren't needed.

This still keeps around a separate set for `new_paths` so that we can
more efficiently iterate over them. It's possible it could benefit from
being a flag in the same set, but it's less obvious.
2026-07-27 15:07:03 -07:00
JosephandGitHub ec76f095ab docs: document query-only href resolution and fix with-vercel-blob (#96280)
Closes: https://github.com/vercel/next.js/issues/96204

The fix to manage rewrites introduced in
https://github.com/vercel/next.js/pull/82236 PR made it so that,
push/replace without explicit pathname broke the `as` pattern, for
example in `with-vercel-blob`.

Also documents the resolution rule in the `useRouter` API reference.
2026-07-27 17:57:37 -04:00
Jimmy MillerandGitHub 0df0a5e4f1 Micro-optimization for string format (#95774)
I get this is a bit of a silly change in some ways. It isn't going to
have some massive impact. But using dhat I got these stats on a smallish
sample nextjs project

| metric | A (parse.rs change) | B (baseline) | delta |
| ------------------------ | -------------------- | ------------ |
------------------- |
| total bytes allocated | 438.4 MiB | 442.6 MiB | -4.3 MiB (-0.94%) |
| total allocation count | 1,672,688 | 1,872,347 | -199,659 (-10.66%)

Eliminating ~200k allocations from this small change just felt worth it.
2026-07-27 16:28:29 -04:00
Vercel Release BotGitHubnext-js-bot[bot] <279046576+next-js-bot[bot]@users.noreply.github.com>
8399ee9150 Upgrade React from 28cd4bb0-20260723 to 756fdd47-20260727 (#96270)
Co-authored-by: next-js-bot[bot] <279046576+next-js-bot[bot]@users.noreply.github.com>
2026-07-27 15:57:43 -04:00
danandGitHub e2dce63f3f [test] Unflake more tests (#96081)
- `detectContentType` test was too aggressive about asserting completion
in 1ms, let's give it several tries.
- We weren't correctly setting up pnpm overrides in tests, which breaks
things whenever canary comes out.
2026-07-27 20:54:10 +01:00
danandGitHub f2ce9d4bfd [test] Run the navigation e2e suite under cacheComponents (#95877)
This test suite used to be disabled in CC runs, which feels like a bit
of a hazard for developing CC.

Let's enable it.
2026-07-27 20:54:10 +01:00
danandGitHub 2dea98ed31 [test] Run more test suites under cacheComponents flag, again (#96196)
Reverts the revert of #95878.

This failed on `main` due to React bugs
(https://github.com/react/react/pull/37061?) which are fixed now.
2026-07-27 20:54:09 +01:00
danandGitHub 962d545a6b [test] Unflake a prefetch-related race (#96222)
Test-only.

The test assertion was racy: it asserted no with-query-param RSC request
during hash-only navigations, but a late prefetch of the
`#link-to-query-param` link makes exactly that request (failed 3/3 on a
prod CI shard).

Assert the actual invariants instead:

- hash-only navigations issue no navigation RSC requests at all
(prefetches, identified by their headers, are ignored)
- a query change *does* issue one — with `prefetch={false}` on the link
so it can't be served from the prefetch cache.
2026-07-27 20:54:09 +01:00
Aurora ScharffandGitHub 1f65c7646e docs(ai-agents): restructure as steps and close 16.2/16.3 coverage gaps (#96247)
### What?

Restructure the AI agents guide into a step-by-step progression and
close the coverage gaps against the 16.2 and 16.3 AI blog posts.

### Why?

The guide read as a feature list and was missing agent-facing features
the blog posts announced: the actionable error menus with the **Copy
prompt** button and per-error `/docs/messages` pages, the `.md` /
`llms.txt` docs access path, and the `.next/dev/lock` behavior that
keeps agents from stacking dev servers.

### How?

Four steps now sequence the setup (bundled docs, runtime visibility,
errors, skills), with the new content folded in where it belongs. The
skills intro states the knowledge-vs-workflow split (bundled docs are
the source of truth, skills are for workflows). Cross-links the instant
navigation AI workflow and the building guide's `--debug-prerender`
section, and the terminal example uses the current message wording
rather than the blog's pre-stable snapshot. The `#existing-projects`
anchor and per-skill anchors are unchanged for published inbound links;
instant-navigation's link to runtime visibility is updated to the new
heading.
2026-07-27 15:51:35 +00:00
Sebastian "Sebbie" SilbermannandGitHub 5d6a82e021 Replace vendored http-proxy with httpxy (#96060)
[`httpxy`](https://github.com/unjs/httpxy) is a faster and actively
maintained variant of `http-proxy`. It also doesn't need any manual
patches to avoid security vulnerabilities in Next.js.

`httpxy` is used by Nuxt and maintained by the Nitro team.
2026-07-27 10:25:54 -04:00
Sebastian "Sebbie" SilbermannandGitHub 9e59ff0a42 Update vendored @mswjs/interceptors to 0.41.9 (#96059)
The previous `@mswjs/interceptors` has a bug where the response it
forwarded never set the [`complete`
field](https://nodejs.org/api/http.html#messagecomplete). This wasn't an
issue so far (or not reported) but is an issue for `httpxy` which relies
on an accurate `complete` field.

Bumping `@mswjs/interceptor` now so that we can switch from `http-proxy`
to `httpxy`.
2026-07-27 10:25:53 -04:00
Andrew ClarkandGitHub 3cd6d4dd1c Track whether runtime data is accessed during prefetch (#95964)
Adds a server-computed signal to static per-segment prefetch responses
that tells the client whether a runtime prefetch request would return
more content than the static response already contains.

The signal is computed by tracking whether the prerender accessed any
data source that hangs during a static prerender but would resolve
during a runtime prerender. For example: cookies, headers, fallback
params, and search params.

The information is encoded two places:

- As a prefetch hint called ShouldAttemptStaticPrefetch. This tells the
client that it's worth attempting to do a static prefetch instead of a
runtime one. It's only an optimization, though: if the static response
ends up being insufficient, the client will follow it up with a runtime
request. It's semantically OK if there are false positives.
- Embedded in the static segment response. This tells the client whether
the static response is missing data that would have been included in a
runtime response. Unlike the prefetch hint, this value must never
falsely claim that no runtime request is needed.

A follow-up will update the client to skip the runtime request using
this information. This commit only encodes the signal into the response.
2026-07-27 09:52:06 -04:00
Tim NeutkensandGitHub 31b78bbed9 Add Instant Insights request timing (#95958)
## Summary

- record deferred Instant Insights work as a separate typed Request
Insights item
- keep validation spans, fetches, timing, and live updates separate from
the foreground request
- label Instant Insights consistently in DevTools and CLI output
- stack this observability change on #95939, which moves validation off
the navigation response path

## Verification

- `pnpm jest --runInBand
packages/next/src/server/lib/trace/request-insights.test.ts
packages/next/src/server/lib/trace/local-span-recorder.test.ts
packages/next/src/next-devtools/dev-overlay/components/request-insights/trace-viewer.test.ts
packages/next/src/next-devtools/dev-overlay/shared.test.ts`
- `NEXT_SKIP_ISOLATE=1 NEXT_TEST_PREFER_OFFLINE=1 pnpm test-dev-turbo
test/development/app-dir/request-insights/request-insights.test.ts`
- `NEXT_SKIP_ISOLATE=1 NEXT_TEST_PREFER_OFFLINE=1 pnpm test-dev-webpack
test/development/app-dir/request-insights/request-insights.test.ts`
- `pnpm --filter=next types`

<!-- NEXT_JS_LLM -->

---------

Co-authored-by: David Ilie <[email protected]>
2026-07-27 07:27:19 -04:00
Sam PoderandGitHub 33a09d53dc [turbopack] Tree-shake module.exports = {...} (CJS) (#95996)
This PR builds on https://github.com/vercel/next.js/pull/95716 by adding
support tree-shaking exports exported using the `module.exports = {...}`
syntax
2026-07-27 00:26:22 -07:00
af3dad8390 [turbopack] Fix de-opt in getChunkRelativeUrl() (#96183)
Used Claude + this tool (https://github.com/vercel/next.js/pull/96158)
to look at Turbopack's runtime. With a large fixture with a lot of
imports, this part was bailing out:

```
{
  "kind": "deopt",
  "bailoutType": "deopt-eager",
  "reason": "wrong map",
  "functionName": "getChunkRelativeUrl",
  "original": {
    "source": "turbopack:///[turbopack]/browser/runtime/base/runtime-base.ts",
    "line": 486,
    "column": 11
  },
  "severity": "high"
}
```

After the fix, this went away.

---------

Co-authored-by: Luke Sandberg <[email protected]>
2026-07-26 23:25:39 -07:00
Andrew ClarkandGitHub af9a392816 Add shell offset to static segment prefetches (#95963)
Updates the per-segment, static prefetch response type to include a
bytes offset for the App Shell. This will allow the client to extract a
shell response from a more complete response, like we already do for
page-level prerender responses.

All of the significant changes are confined to the server, during the
step when a full page prerender is used to create per-segment responses
(collect-segment-data). The overall strategy is the same one used at the
page level. The input data is unblocked in distinct stages, separated by
macrotasks. At each stage, we record the number of bytes that have been
emitted to the output stream. Then, before closing the final stream, we
embed the byte offsets into the response.

During this phase, no actual user code runs: all we're doing is decoding
and reencoding an existing Flight response. So the timing logic is more
straightforward. For example, we don't need to worry about dynamic data
leaking into the result.

This commit is purely an in-place refactor. Subsequent steps will update
the client to take advantage of the new offsets.
2026-07-27 00:26:28 -04:00
Tim NeutkensandGitHub 3f2cf7e1a0 Turbopack: support import.meta.glob caseSensitive option (#96226)
## Summary

Add Vite-compatible `caseSensitive` support to `import.meta.glob()`.
Matching remains case-sensitive by default, while `caseSensitive: false`
applies case-insensitive matching to directory traversal, filenames,
positive patterns, and negative patterns.

The option is included in matcher equality and non-default virtual
module identity, and is exposed through diagnostics, TypeScript
declarations, and the Turbopack options reference.

## Verification

- `cargo test -p turbo-tasks-fs glob`
- `cargo nextest run -p turbopack-tests -E "test(import_meta_glob) and
not test(import_meta_glob_errors)"`
- `cargo fmt --all -- --check`
- `pnpm build-all` and `pnpm --filter=next types` are blocked by
current-canary `sharp.default` type errors in
`packages/next/src/server/image-optimizer.ts`

<!-- NEXT_JS_LLM -->
2026-07-26 19:46:17 -04:00
next-js-bot[bot]andGitHub 367e5215bf v16.3.0-canary.97 v16.3.0-canary.97 2026-07-25 23:35:04 +00:00
d2ee326a0b fix(sandbox): release one-shot timeout ids after they run (#96161)
### What?

Fire-and-forget `setTimeout` calls in the Edge runtime sandbox (e.g.
from middleware) are tracked forever by Next.js's sandbox
`TimeoutsManager`, even after the timeout has run. In a long-lived
standalone/self-hosted server this makes tracked timeout resources grow
monotonically, producing step-like heap growth and eventual OOM.

### Why?

`TimeoutsManager.add()` pushes each timeout id into a `resources` array.
When a one-shot timeout fires naturally, `webSetTimeoutPolyfill`'s
`finally` clears the underlying Node timer (the workaround for
nodejs/node#53335) but never removes the id from the manager. The id is
dropped only if user code calls `clearTimeout(id)` or the whole module
context is torn down (`clearModuleContext`, introduced in #57235). The
natural-completion path — module context stays alive, timeout finishes
on its own — was missing, so ids accumulate for the lifetime of the
process.

### How?

- `TimeoutsManager.create()` now wraps the callback so the timeout
untracks its own id once it has run. The Node-timer workaround and the
`this`/args binding in `webSetTimeoutPolyfill` are preserved.
- Refactored the base `ResourceManager.remove()` into `untrack()` (stop
tracking) + `destroy()`, so natural completion can release tracking
without a redundant `clearTimeout`.
- Intervals are intentionally unchanged: they fire repeatedly and must
remain tracked until `clearInterval`/`removeAll`.
- Added a unit test covering natural release, no monotonic growth across
repeated rounds, the `clearTimeout`/`remove` path, callback `this`/args
binding, and that intervals stay tracked.

Verified: `pnpm --filter=next types` clean, prettier/eslint clean, and
the new unit test fails on the current behavior (reverting only the
release logic) and passes with the fix.

Fixes #95094

---

**Update:** this branch now lands @berry95's commit from #94794 first
(`Set`-based resource tracking, which also makes `untrack`/`remove`
O(1)), with the fix rebased on top.

---------

Co-authored-by: Berry Kim <[email protected]>
Co-authored-by: Dan Abramov <[email protected]>
2026-07-25 19:42:17 +01:00
b51ca60fd4 Fix dev overlay symbolication for project paths needing percent-encoding (#96221)
In a project whose absolute path contains characters that percent-encode
in URLs — e.g. a space — the dev overlay showed no code frame and raw
`file://` URLs instead of source locations for server frames.

Two encoding mismatches caused this:

- Turbopack's `trace_source` percent-decoded the source map's original
file URL before comparing it against the still-encoded project root URI,
so the containment check failed ("Original file ... outside project")
for any project path with such characters. The comparisons now stay in
the encoded domain and only the outputs are decoded back into paths.

- React synthesizes stack frame `file:` URLs by prepending the scheme to
a filesystem path, so the overlay receives URLs with e.g. raw spaces.
These aren't well-formed, and they don't match the `url.pathToFileURL`
form Node.js keys its source map cache by. The overlay middleware now
re-encodes them through WHATWG URL parsing, which tolerates such input.

## Test Plan

New tests.

Known cases that remain broken are commented out with a TODO.
These could be resolved if https://github.com/react/react/pull/37105 or
equivalent lands.

## Before

<img width="825" height="732" alt="Screenshot 2026-07-25 at 14 39 18"
src="https://github.com/user-attachments/assets/0f119e52-ec86-495a-bf5c-5339faa1d606"
/>


## After

<img width="1126" height="733" alt="Screenshot 2026-07-25 at 14 58 14"
src="https://github.com/user-attachments/assets/a263ee8a-4cfe-4358-9a8e-2e58b4b16aa5"
/>

Co-authored-by: Claude Fable 5 <[email protected]>
2026-07-25 18:36:45 +01:00