Commit Graph

35737 Commits

Author SHA1 Message Date
Niklas Mischkulnig 606c4ebb36 Turbopack: cross-module constants (#90300)
Closes https://github.com/vercel/next.js/issues/92082

This is now a proper compile-time constant:
```js
import { IS_DEV } from './other'
if (IS_DEV) { // statically evaluates to `true`
  console.log('x')
} else {
  require("library") // not bundled
}
console.log(IS_DEV); // is replaced with console.log(true);
```
```typescript
// other.ts
export const SOME_VALUE = 'x'

const node_env = process.env.NODE_ENV
const development_ent = 'development'
export const IS_DEV = node_env === development_ent
```

You can use code to compute constants just fine, and use any existing
constants such as `process.env.NODE_ENV`.
Currently, you can't use imports to other constants modules, but we can
add that later on.

We can't perform this constants check for every single import, so you
need to either
- have `UPPER_CASE` import names as in the example above
- or use `import { lower } from './other' with { turbopackConstants:
'true' }`

Then that referenced module will be analyzed for constants, and if the
referenced export is a constant, it will participate in constant
inlining just as `process.env.FOO`.

This also works fine with barrel imports, you can still do `import {
IS_DEV } from './barrel.js'` and it will find the `constants.js` file
which in itself will indeed only have constants exports.

Because it's not always opt-in at the import site, we can't
automatically make non-constant exports an error. For that you have to
add `'use turbopack: constants'` at the top of the module, which will
make it an error if any constant import references that module, and the
module has any non-constant exports:
```
error - [analysis] /turbopack/crates/turbopack-tests/tests/snapshot/comptime/cross-module/input/other.constants.js:8:7 
Export NO_CONSTANT is not a constant
  
       4 | const development_ent = 'development'
       5 | 
       6 | export const IS_DEV = node_env === development_ent
       7 | 
         +        v--------------------------------v
       8 + export const NO_CONSTANT = globalThis.foo
         +        ^--------------------------------^
       9 | 
  
  It was analyzed to be FreeVar(globalThis)["foo"]
  
  
  Import trace:
    test:
      ./turbopack/crates/turbopack-tests/tests/snapshot/comptime/cross-module/input/other.constants.js
      ./turbopack/crates/turbopack-tests/tests/snapshot/comptime/cross-module/input/index.js
```

Some prior art:
https://rspack.rs/blog/announcing-1-5#const-inline-optimization,
https://rspack.rs/config/optimization#optimizationinlineexports

No compile-time impact on a big app:
```
canary dfbc3dc6b7: 
438.11s user, 68.35s system, 801% cpu, 1:03.22 total
439.44s user, 71.47s system, 756% cpu, 1:07.56 total
440.32s user, 68.57s system, 750% cpu, 1:07.81 total

constants 2d7eb8220d298133212813b7267a5847eed03a22
433.22s user, 65.44s system, 800% cpu, 1:02.32 total
440.52s user, 67.68s system, 770% cpu, 1:05.95 total
433.13s user, 69.47s system, 776% cpu, 1:04.74 total
```

a8620556af64ef550373cca1e1fc701a1a8583cc
2026-08-19 14:01:05 +02:00
Niklas Mischkulnig da4888c8df test: better isolate concurrent-install suite (#97546)
these were failing with 50% probability since today, for some reason:
https://app.datadoghq.com/ci/test/runs?query=test_level%3Asuite%20%40test.service%3Anextjs%20%40test.type%3Aturbopack%20%40test.suite%3Aconcurrent-install&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&start=1786528015972&end=1787132815972&paused=false
2026-08-19 13:15:13 +02:00
Joseph 78b11c37e6 docs: outlining and lcp (#96942)
LCP can be affected if the LCP element is sitting within a Suspense
boundary, even if its nothing but static content, if the static content
is "too large".

Apps should prefer, as long as possible, to not have a Suspense boundary
around the LCP element.

-
https://react.dev/reference/react/Suspense#what-activates-a-suspense-boundary

> Waiting for a large boundary’s HTML to arrive during streaming server
rendering. Sending HTML takes time, so a boundary with enough content
activates even when nothing in it suspends. React reveals the content as
the HTML arrives.
2026-08-19 10:31:44 +02:00
Josh Story 4a95af8084 Fix use cache prerender signal retention (#97476)
## Summary

After a fallback-shell cache prerender completes, snapshot whether its
timeout fired and then abort the existing timeout controller when it
participates in an `AbortSignal.any()` composite. This triggers the
composite so React removes its abort listener; no additional controller
or signal is needed. Cache prerenders without a dynamic-access source
keep their existing direct timeout signal.

Node retains non-empty composite abort signals while they have abort
listeners. React attaches such a listener during `prerender()` and
removes it when the signal aborts, so aborting the already-owned timeout
source releases the successful render. Snapshotting `didTimeout` first
keeps cleanup aborts distinct from real timeouts.

This preserves the early aborted-prerender guard from #96426, which
prevents a cache fill that starts after its outer prerender aborts from
caching an empty React stream.

Fixes #97363
Related #97464
Alternative to #97391

## Verification

- `pnpm --filter=next build`
- `pnpm test-start-turbo
test/e2e/app-dir/use-cache-after-uncached-io/use-cache-after-uncached-io.test.ts`
- `pnpm test-start-turbo
test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts`
- Actual vendored React `prerender()` GC probe on Node 20.19.5 and
22.20.0: valid preludes, no cleanup errors, and 0/100 composite signals
retained while their source controllers remained reachable.

<!-- NEXT_JS_LLM -->
2026-08-19 09:32:39 +02:00
Benjamin Woodruff ‮ b677feb02f Turbopack: More aggressively debounce filesystem watch events if we detected changes to node_modules (#96116)
Previously, we were debouncing update by sleeping 1ms at a time on macos
and windows, and 10ms at a time on Linux.

During a slow `pnpm install`, or a `git checkout`, this could cause us
to do a bunch of extra throwaway work.

Changes:
- Increase the debounce interval to a consistent 10ms everywhere. This
should still be small enough that it's not noticable on macos or
windows.
- If an event touches `node_modules`, there's a good chance that a
package manager is running and many other files will be modified, so
extend the batch deadline by 200ms instead of 10ms.
- Because there's a chance that the batch deadline could get extended
indefinitely (this was always possible, just more likely now) include a
compilation event that gets logged after 5 seconds.
2026-08-18 17:05:14 -07:00
KAM dc5fe22519 docs: document metadata pagination field (#95509)
### What?

Documents the `pagination` metadata field in the `generateMetadata` API
reference.

### Why?

The `Metadata` type supports `pagination.previous` and
`pagination.next`, but the Metadata Fields documentation did not list
the field.

Fixes #83264

### How?

Adds a `pagination` section with an example and the generated `<link
rel="prev">` / `<link rel="next">` output.

### Tests

- `pnpm prettier --check
docs/01-app/03-api-reference/04-functions/generate-metadata.mdx`
- `pnpm lint-eslint
docs/01-app/03-api-reference/04-functions/generate-metadata.mdx`
- `git diff --check HEAD~1..HEAD`

<!-- NEXT_JS_LLM_PR -->
2026-08-19 01:29:32 +02:00
next-js-bot[bot] d07b580f99 v16.3.1-canary.24 v16.3.1-canary.24 2026-08-18 23:22:34 +00:00
niketchandivade 0eb3775416 style(examples): remove redundant justify-content declaration (#97222)
## Summary

While reviewing the examples in the Next.js repository for potential
improvements, I noticed a redundant `justify-content` declaration in the
`.submit` styles.

## Changes

- Removed the redundant `justify-content: flex-end` declaration.
- Kept `justify-content: space-between`, which overrides the previous
declaration.

## Testing

No functional changes. This is a CSS cleanup only.

Co-authored-by: Marcos Hernanz <96699542+marcoshernanz@users.noreply.github.com>
2026-08-18 22:41:00 +00:00
Rich Haines f0df931a6e docs: rename Vercel Edge Config to Global Config in redirecting guide (#97456)
## What

Vercel's **Edge Config** product is now **Global Config**. This updates
the two stale-name occurrences flagged in the redirecting guide.

The URL was already migrated in #96723 (`/docs/edge-config/get-started`
→ `/docs/global-config/get-started`), but that pass only rewrote link
targets — the visible product name and the SDK import in the code
samples still said `Edge Config` / `@vercel/edge-config`, so the page
read *"Vercel's Edge Config"* while linking to the Global Config docs.

## Changes

`docs/01-app/02-guides/redirecting.mdx`, in *Managing redirects at scale
→ Creating and storing a redirect map*:

| | Before | After |
| --- | --- | --- |
| Prose | Vercel's [Edge Config] | Vercel's [Global Config] |
| `proxy.ts` / `proxy.js` samples | `import { get } from
'@vercel/edge-config'` | `import { get } from '@vercel/global-config'` |

The link target, the neighbouring [Redis](https://vercel.com/docs/redis)
link, and the surrounding sentence structure are unchanged.

## Why the package import changed too

The renamed package is published and is the same library —
`@vercel/global-config@1.5.1`, from `vercel/storage` at
`packages/global-config`, matching `@vercel/edge-config@1.5.1` — and it
exports the same `get`. Vercel's own [Global Config
quickstart](https://vercel.com/docs/global-config/get-started) now uses
`import { get } from '@vercel/global-config'`. Leaving the old specifier
would have left the sample importing a legacy package name on a page
that calls the product Global Config. `@vercel/edge-config` is not yet
formally deprecated on npm, so this is a naming-consistency change
rather than a fix for broken code.

## Both flagged pages

Two docs paths were flagged — `/docs/app/guides/redirecting` and
`/docs/pages/guides/redirecting` — but they share one source.
`docs/02-pages/02-guides/redirecting.mdx` is a generated stub (`source:
app/guides/redirecting`, carrying the *DO NOT EDIT* banner), and the
affected paragraph is not wrapped in `<AppOnly>`/`<PagesOnly>`, so it
renders on both. Editing the App Router source fixes both pages; no
separate edit to the Pages file is needed or allowed.

## Verification

- `grep -rn "Edge Config\|edge-config" docs/` → no matches remain.
- `prettier --check` and `alex` pass on both files; the pre-commit
`lint-staged` (prettier + eslint) pass ran clean.
- `https://vercel.com/docs/global-config/get-started` → `200`; the old
`/docs/edge-config/get-started` → `308`, confirming the link already
points at the final target.

Docs-only change; no code or tests affected.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Rich Haines <22930449+molebox@users.noreply.github.com>
2026-08-18 15:37:43 -07:00
niketchandivade 520de42f19 fix: improve form accessibility by associating labels with inputs (#96335)
## Summary

This PR improves the accessibility of the `with-apivideo-upload` example
by associating form labels with their corresponding form controls using
the `htmlFor` attribute.

## Changes

- Added `htmlFor="link"` to the "Play button color" label.
- Added `htmlFor="linkHover"` to the "Buttons hover color" label.
- Added an `id` to the "Hide controls" checkbox and associated its label
using `htmlFor`.

## Why

Associating labels with their corresponding form controls improves
accessibility by:

- Allowing screen readers to correctly announce form labels.
- Enabling users to focus or toggle controls by clicking their labels.
- Following HTML and WCAG best practices for accessible forms.

## Before

- Labels were visually displayed but were not programmatically
associated with their respective inputs.

## After

- Each label is associated with its corresponding form control via
`htmlFor` and `id`, improving accessibility without changing
functionality.

### Testing

- Verified that clicking each label focuses or toggles the associated
control.
- No visual changes.

Co-authored-by: Marcos Hernanz <96699542+marcoshernanz@users.noreply.github.com>
2026-08-18 22:33:46 +00:00
Sean Beirnes c87d742332 [docs] fix: grammar typos in linking and navigating guide (#95544)
### What?

Fix grammar issues in the "Linking and Navigating" guide.

### Why?

This sentence had a few grammar issues:
- "makes" should be "make"
- "a server-rendered apps" should be "server-rendered apps"
- "it enables" should be "they enable"

### How?

Updated the sentence in
`docs/01-app/01-getting-started/04-linking-and-navigating.mdx` to
correct grammar and improve subject-verb agreement.

---------

Co-authored-by: Marcos Hernanz <96699542+marcoshernanz@users.noreply.github.com>
2026-08-18 22:33:00 +00:00
niketchandivade 0ff70fd55b fix(examples): correct error message typo (#97223)
## Summary

While reviewing the examples in the Next.js repository for potential
improvements, I noticed a typo in the error message displayed when an
unexpected error occurs.

## Changes

- Corrected `"An unexpected error happened occurred:"` to `"An
unexpected error occurred:"`.

## Testing

No functional changes. This is a text-only fix.

Co-authored-by: Marcos Hernanz <96699542+marcoshernanz@users.noreply.github.com>
2026-08-18 22:31:36 +00:00
이동현 fe87d7ac75 docs: fix typos in example links (#97149)
Fixes typos in the `analytics.tsx` link syntax in the two Segment
example READMEs.

Co-authored-by: Marcos Hernanz <96699542+marcoshernanz@users.noreply.github.com>
2026-08-18 15:08:17 -07:00
Josh Story 35d91fcaae Model prerenders as render candidates (#97431)
## Summary

This is the behavior-preserving base of a four-PR stack that introduces
explicit prerender matching policy without coupling the foundational
refactor to the proposed API.

The build currently uses `PrerenderedRoute` values for two related but
distinct concepts:

- a logical request matcher that says which parameterized URL shapes the
route can handle
- a build-time render candidate that may or may not become a persisted
prerender artifact

A candidate is not guaranteed to be an output. It can be rendered only
to validate a shell, discarded, and still leave behind a matcher that
tells future requests to block.

The new relationship is:

```text
logical pathname matcher
  -> zero or more render candidates
    -> zero or more persisted artifacts
```

## Concrete example

Consider `/[top]/items/[bottom]`:

```ts
export function generateStaticParams() {
  return [{ top: 't1', bottom: 'b1' }]
}
```

Static-path generation may consider three shapes:

| Shape | Purpose |
| --- | --- |
| `/[top]/items/[bottom]` | A generic shell candidate and logical
matcher |
| `/t1/items/[bottom]` | A shell after resolving `top` |
| `/t1/items/b1` | A concrete build-time prerender |

Suppose the generic candidate renders an allowed empty shell. The build
should discard that candidate artifact and use blocking behavior for the
generic matcher. It should not remove `/[top]/items/[bottom]` from the
valid matcher set, and it should not discard the concrete `/t1/items/b1`
artifact.

This is why the build needs two sets:

- route matchers, which describe valid request shapes
- prerender candidates, whose render results determine whether an
artifact is retained and can refine inferred fallback behavior

## Variants compatibility

Variants will make pathname-only candidate maps insufficient. Several
variant combinations can share `/t1/items/b1` as their logical pathname
while writing distinct artifacts under variant-specific output paths.

This PR keeps the route matcher keyed by logical pathname but retains
every candidate associated with it. Candidate finalization can therefore
evaluate each variant artifact independently without changing the route
tree's matcher set.

## Behavior preservation

This PR does not add a user-facing API or change `generateStaticParams`
semantics:

- a usable static shell remains a fallback prerender
- an allowed empty shell is discarded and represented by a blocking
matcher
- a route that requires a non-empty shell still fails validation
- unresolved matchers remain gated by route-level PPR support
- the most-specific shell continues to supply first-writer-wins metadata
such as prefetch hints

Render results can decide whether a candidate artifact is published and
can refine inferred matcher behavior, but they do not remove the logical
route matcher itself.

## Stack plan

1. **#97431 — model prerenders as render candidates:** land the
behavior-preserving matcher/candidate separation and post-render
finalization first.
2. **#97393 — add the experimental matcher API:** add `unstable_matcher`
and `unstable_generateMatcher`, policy aggregation, validation, and
local diagnostics.
3. **#97426 — test complex route shapes:** add test-only coverage for
catch-alls, optional catch-alls, root parameters, and parallel slots.
4. **#97427 — test foreground policy behavior:** add test-only coverage
showing blocking misses generate before responding while fallback misses
return the shared shell immediately.

This layering lets the internal model be reviewed and landed
independently of the API design. The upper test PRs validate the final
behavior without increasing the implementation diff.

## Verification

- `pnpm --filter=next types`
- `pnpm test-start-turbo
test/e2e/app-dir/sub-shell-generation/sub-shell-generation.test.ts`
- `pnpm test-start-turbo
test/e2e/app-dir/segment-cache/prefetch-inlining/prefetch-inlining.test.ts`

<!-- NEXT_JS_LLM -->
2026-08-18 12:31:47 -07:00
Niklas Mischkulnig 9dfbc93848 Turbopack: support character class ranges in regex (#97502)
Closes https://github.com/vercel/next.js/issues/97467

---------

Co-authored-by: vercel-fleet[bot] <308483924+vercel-fleet[bot]@users.noreply.github.com>
2026-08-18 19:38:41 +02:00
David Alexandru Ilie f0088acc7d docs: warn when catching permanentRedirect (#97496)
## Summary

Document that `permanentRedirect()` throws and should be called outside
a broad `try/catch` block.

The `redirect()` and `notFound()` references already explain this
control-flow requirement. The `permanentRedirect()` reference omits it,
so application error handling can accidentally suppress the redirect.

Align the page with the `redirect()` reference by adding a dedicated
Behavior section for the try/catch guidance and temporary-redirect
cross-link.

## Verification

- Prettier, ESLint, and `git diff --check`
2026-08-18 19:39:04 +03:00
Hendrik Liebau b18acf6712 Remove the development debug channel persistence (#97510)
Documents are now served with `no-store` in development, so a browser
never restores one from its HTTP cache and the page scripts never
re-execute against a debug channel that has already delivered its data.
The persistence and restore machinery that existed for that case has no
remaining trigger, so this removes it: the `IndexedDB` write scheduled
on every page load, the cache-restore detection across
`PerformanceNavigationTiming` fields and `deliveryType`, the `pageshow`
deferral for browsers that populate those fields late, and the
`location.reload()` fallback for a missing entry. It was built up over
#92892, #93486, #94128, #94317 and #94243, and takes `debug-channel.ts`
from 535 lines to 121.

The per-consumer `tee()` and the LRU-bounded pair map stay. They were
added for an unrelated reason, namely that one response can be decoded
more than once, so this is not a revert to the state before the
persistence landed. The rejection handler on `writer.closed` also stays,
because an errored stream would otherwise surface as an unhandled
rejection now that nothing else observes it.

`bfcache-regression` keeps the original regression test, which loads a
page, navigates away, comes back and asserts that the counter is still
interactive. That case now fails if the development `Cache-Control`
value ever goes back to `no-cache`, because the restored document would
block hydration with no reload to recover, so it is worth keeping as is.
The other three tests lose their premise and are deleted along with the
routes only they used: the pruning case that was skipped when the header
changed, the recovery case that needs a restore path to recover into,
and the streaming case that guarded the detection against treating an
in-flight response as a restore. The `large-debug-data` route goes too.
It existed only to make the persistence write expensive enough to
profile by hand when it moved to `IndexedDB`.
2026-08-18 16:54:46 +02:00
Hendrik Liebau 2839982a03 Stop the browser from restoring stale pages in development (#97505)
Development responses used `no-store, must-revalidate` until #88182
tried `no-cache, must-revalidate` behind
`experimental.devCacheControlNoCache`, and #91503 removed that option
and hard-coded the `no-cache` value everywhere. That was right for
static assets and wrong for documents. A browser may reuse a stored
response for a history navigation without revalidating it, and
development documents are streamed without an `ETag`, so there is
nothing to revalidate against. Going back therefore restored the
document the browser had stored earlier and showed output from before
the latest edit, and it is also what forced the debug channel
persistence workarounds in #92892, #93486 and #94243.

Documents and RSC or data responses now use `no-store` again, set in
`app-page-runtime.ts` for app pages, in `pages-handler.ts` for pages,
and in the legacy render pipe in `base-server.ts` so that the three do
not drift apart. None of them ever serves a static asset, so assets keep
`no-cache, must-revalidate` from the `nextStaticFolder` branch in
`router-server.ts` and stay cacheable: they are revalidated against the
`ETag` that `serveStatic` adds and reused from a `304` instead of being
downloaded again on every page load. `must-revalidate` is left off the
document value, because it only governs reuse of an already stale stored
response and nothing is stored any more.

A back navigation is no longer instant, since the document is fetched
again instead of being restored locally.
`test/development/dev-cache-control` covers both sides of that
trade-off: an edit that is visible after a back navigation, and
unchanged assets that still come back as `304`. It replaces
`dev-cache-control-no-cache` and asserts the header for both routers as
well, so there is one suite instead of two with nearly the same name.

A development document is now never restored from the HTTP cache, so the
debug channel persistence has no remaining trigger and its `IndexedDB`
write on every page load is no longer needed. Removing it is a follow-up
on top of this change. The pruning and recovery test in
`bfcache-regression` is the one case whose premise disappears entirely,
and it is skipped here with a note to delete it along with the
persistence.

closes #96503
2026-08-18 16:54:45 +02:00
Niklas Mischkulnig da50acde10 Turbopack: gracefully handle outputFileTracingIncludes matching a symlink (#97507)
Closes https://github.com/vercel/next.js/pull/96999

Make sure we don't do `.read().hash()` which is incorrect with symlinks.
Instead, hash the symlink itself instead of its target. This is what we
copy into the function source anyway

---------

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-18 15:59:26 +02:00
Fabian Hiller 36dd9e21d6 docs: mention Valibot as validation library option in forms guides (#97468)
The forms guides recommend validating with a library like Zod. This
broadens the wording to also mention Valibot, which covers the same use
case with a smaller bundle footprint. The authentication guide already
lists Zod or Yup, so Valibot is added to that list as well. The existing
Zod examples are unchanged. I'm the author of Valibot.

- Ran `prettier --check` on the touched files with the repo's pinned
version.
2026-08-18 15:44:20 +02:00
next-js-bot[bot] d00eb6b6c4 v16.3.1-canary.23 v16.3.1-canary.23 2026-08-18 11:36:05 +00:00
David Alexandru Ilie 682b03f73f Trace lazy App Route module loading (#97439)
### What?

Adds a public OpenTelemetry span for the cold evaluation of an App
Route's
lazy userland module:

- span type: `AppRouteRouteModule.loadUserland`
- display name: `load app route module`
- route-specific attribute: the normalized `next.route` pattern

### Why?

`LoadComponents.loadRouteModule` measures loading Next.js's compiled
route
entry. App Routes then defer the actual userland module behind a
`LazyModule`, so expensive top-level module evaluation can occur after
that
existing span has ended and remain invisible in traces.

### How?

The App Route module wraps only its lazy userland factory with the
Next.js
tracer. `LazyModule` still memoizes that factory, so each route-module
instance
emits one span for its successful cold initialization and warm requests
reuse
the initialized value. The HMR getter remains untraced, and the span
records no
resolved filename or request URL.

The integration fixture uses top-level await to exercise the
asynchronous
module path. Coverage verifies the span is a descendant of the request,
appears exactly once across cold and warm requests, and works with the
direct
entrypoint handler.

### Verification

- `pnpm exec jest packages/next/src/server/lib/trace/tracer.test.ts
--runInBand`
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts -t 'should
trace cold App Route module loading'`
- `HEADLESS=true pnpm test-dev-webpack
test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts -t 'should
trace cold App Route module loading'`
- `HEADLESS=true pnpm test-start-turbo
test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts -t 'should
trace cold App Route module loading'`
- `HEADLESS=true pnpm test-start-webpack
test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts -t 'should
trace cold App Route module loading'`
- Exact-head GitHub build-and-test run `31950299347`: 102/102 jobs
passed.
- Exact-head Generate Stats run `31950299340`: passed.
2026-08-18 12:33:22 +01:00
Sebastian "Sebbie" Silbermann 1941060ff4 [ci] Wait for the next version to be available on npm before deploy tests (#97460)
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
2026-08-18 13:20:44 +02:00
Joseph 67eede2b8d docs: fix stale Dev Overlay README link in contributing guide (#97486)
In #80281 we moved the README from
`../../packages/next/src/client/components/react-dev-overlay/README.md`
to `packages/next/src/next-devtools/README.md`, but did not update the
link in the contributing guide.
2026-08-18 11:53:49 +02:00
Joseph e551922083 docs: app router reference accuracy (#97477)
- Adding
docs/01-app/03-api-reference/05-config/01-next-config-js/cacheMaxMemorySize.mdx
- Various use cache snippet fixes
- Break nuance for fetch default
- Point to correct turbopack cache flags
2026-08-18 11:52:26 +02:00
Niklas Mischkulnig d6ae012436 test: Don't wait trying to load non-existing images (#97488)
These tests were timing out because they are suddenly waiting for a very
long DNS timeout

-
https://github.com/vercel/next.js/actions/runs/32116429187/job/95655640990?pr=90300
-
https://github.com/vercel/next.js/actions/runs/32116429187/job/95655640961?pr=90300
2026-08-18 11:47:21 +02:00
Niklas Mischkulnig 0b7e96f41f test: cover dynamic APIs with i18n base path (#97367)
Recreation of https://github.com/vercel/next.js/pull/96910

### What?

Adds a test for i18n basepath fallbacks.

### Why?

We had this test in `vercel/vercel`:
https://github.com/vercel/vercel/blob/main/packages/next/test/fixtures/00-i18n-basepath-fallback-false-404/index.test.js

We're getting rid of it in favor of moving it to here in Next.js. This
new test is meant to be equivalent to the original.

Co-authored-by: Anthony Shew <anthonyshew@gmail.com>
2026-08-18 10:20:11 +02:00
next-js-bot[bot] 6be687032b v16.3.1-canary.22 v16.3.1-canary.22 2026-08-17 23:22:17 +00:00
hardfist 9e1dc0ebe4 fix(turbo-persistence): allow 32-bit usize conversion (#97288)
### What?

Change the pointer-width assertion in `usize_from_u32` from `<` to `<=`,
allowing the conversion to compile on 32-bit targets.

### Why?

The surrounding comment states that Turbopack assumes a pointer width of
at least 32 bits, but the strict comparison rejects exactly 32-bit
targets. Since `u32` and `usize` have the same width on these targets,
the conversion remains lossless.

This currently causes `rspack-turbo-persistence` to fail to compile with
`error[E0080]` in Rspack's [`i686-pc-windows-msvc` CI
job](https://github.com/web-infra-dev/rspack/actions/runs/31665978273/job/94340544786#step:25:446).

### How?

Allow equal source and target bit widths in the compile-time assertion.
2026-08-17 16:55:49 +00:00
Niklas Mischkulnig d8234277bd test: restore missing NFT exports* unit fixtures (#97459)
### What?

Restores the missing `node_modules` fixtures for the `exports`,
`exports-fallback`,
`exports-only` and `exports-path` cases in `turbopack-tracing`'s NFT
unit test suite
(`tests/unit.rs`), and enables the two that pass as-is (`exports-only`,
`exports-path`).

Co-authored-by: vercel-fleet[bot] <308483924+vercel-fleet[bot]@users.noreply.github.com>
2026-08-17 18:41:51 +02:00
Zack Tanner 10439f3c6f [ci] Fix backport canary release dispatch (#97383)
## Summary

- Use the job-scoped `GITHUB_TOKEN` with explicit `actions: write`
permission to dispatch `trigger_release.yml` after a stable backport.
- Avoid requesting Actions write access from the release GitHub App,
which currently prevents the dispatch token from being created.
- Preserve the existing forced preminor canary release behavior.

This fixes the failure that left canary semver-behind stable after the
`v16.3.1` backport. The evaluation succeeded, but the dispatch failed
while creating the App token in the [failing dispatch
job](https://github.com/vercel/next.js/actions/runs/31750536217/job/94615208366).

## Verification

https://github.com/vercel/next.js/actions/runs/31830288265/job/94864091005
```
should_dispatch=true
reason=Dispatching canary preminor release because stable release 16.3.1 is ahead of 16.3.1-canary.17
released_version=16.3.1
current_canary_version=16.3.1-canary.17
dispatch_input=false
auto_dispatch_enabled=true
```
2026-08-17 06:56:34 -07:00
Luke Sandberg 1c50e09d1d turbo-tasks-backend: Enforce that tasks exist when accessing them (#96043)
Change the semantics of `task` so that it enforces that tasks actually
exist (either in memory or storage). For the rare cases we expect to be
creating tasks, have callers call `get_or_create_task`

generally creating a task is _racy_ so it is always `get_or_create` but
when doing things like updating the aggregation graph or reading cells,
we _know_ it must exist so assert it.

Currently this is a `debug_assert` but with the GC we may update it to a
full assert.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-16 19:53:17 -07:00
Luke Sandberg 8a473f452a turbo-tasks-backend: add persistence delete/tombstone plumbing for GC (#95975)
## What

The persistence-layer mechanism to tombstone (delete) tasks from the
on-disk cache. A GC-collected task is represented as a
`SnapshotItem::Delete` that rides the same streaming iterator
`save_snapshot` already consumes for puts, so tombstones are applied in
the same commit/batch as the snapshot with no side-channel.

- `SnapshotItem` becomes an enum: `Put { … }` (the existing path) and
`Delete(TaskDeletion)`.
- `TurboWriteBatch::delete/value_delete` passthrough to
`turbo_persistence`.
- `save_snapshot` applies deletes inline in the parallel put phase


## Note

`SnapshotItem::Delete` is `#[allow(dead_code)]` here: this PR lands the
persistence-side *handling* and its tests, but nothing *emits* a
`Delete` yet. The GC pass that soft-deletes tasks and serializes them to
`Delete` lands in the next PR in the stack. So this PR is a no-op at
runtime.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-16 19:53:17 -07:00
Luke Sandberg 863a0adaa2 turbo-persistence: add key-value tombstones for MultiValue families (#96929)
Add a new `tombstone` format to the persistence layer so we can delete
key-value pairs out of MultiValued tables

This is in service of the upcoming GC support, but also fills a basic
API gap in the db.

To delete a key-value-pair you need to call `value_delete` and currently
the values are limited to only those that are able to be stored inline
in key blocks. This is a non-trivial limitation but it fits our current
usecase, and makes the compaction/query algorithms a bit simpler (don't
need to 'resolve' values)

One non-trivial complexity was maintaining the 'FixedLayout' block
optimization for a mix of tombstones and values, so now we support a
slightly different mode where all keys have the same length but possibly
different types.

Finally, this branch solves a problem with deleting tombstones.
Tombstones 'shadow' older values and allow us to drop them during
compaction. With GC getting ready to start writing tombstones the risk
becomes 'when can we delete a tombstone! This is solved
probabilistically using the amqf filters, during compaction we drop
tombstones if they could not possibly match anything in an older SST.
Without this, tombstones in the TaskCache table would fill up over time.
2026-08-16 17:28:16 -07:00
next-js-bot[bot] d45672c0bc v16.3.1-canary.21 v16.3.1-canary.21 2026-08-16 23:21:52 +00:00
Hendrik Liebau 5817bd1def Anchor the async local storage instances to global symbols (#97255) 2026-08-16 23:15:51 +02:00
Josh Story c18acf5cef test: deflake use-cache-size-zero warm reload (#97421)
## Summary

Keep the first warm reload assertion focused on browser-visible
stale-while-revalidate behavior, then poll the route with independent
HTTP requests until a later response observes the fresh value. After
convergence, perform one final browser reload to confirm the
user-visible path also receives an updated value.

This avoids fixed delays, cache debug logging, and repeated browser
navigations that can cancel or outpace background regeneration. The
convergence poll uses the standard 3-second `retry()` window and returns
as soon as freshness is observed; the browser only reloads again after
that condition is satisfied.

## Verification

- `__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true NEXT_TEST_CI=true
HEADLESS=true pnpm test-dev-webpack
test/development/app-dir/use-cache-size-zero/use-cache-size-zero.test.ts`
- `__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true NEXT_TEST_CI=true
HEADLESS=true pnpm test-dev-turbo
test/development/app-dir/use-cache-size-zero/use-cache-size-zero.test.ts`

<!-- NEXT_JS_LLM -->
2026-08-16 14:40:01 +00:00
Andrew Clark dd76599c34 Scaffolding for concurrentRouterQueue flag (#97413)
Sets up the scaffolding for a rewrite of the client router's queue.

I've named the new module concurrent-router-queue.ts and the existing
module sequential-router-queue.ts, since the main goal of the new one is
to improve the router's integration with React's concurrent rendering
model. The experimental flag is likewise named `concurrentRouterQueue`.

There's no implementation yet; all router operations throw an error when
the flag is enabled.

The main entry point into the router is navigator.ts. The flag swaps
between implementations at the module level to ensure the inactive code
is eliminated from the final bundle.
2026-08-15 23:46:34 -04:00
Andrew Clark 5a3a3d1972 Reorganize client router modules (#97402)
Pure structural refactor, no behavior changes.

This continues some of the clean-up work I've been doing in the client
router now that more of the implementation has settled, post-Segment
Cache and Instant Navigations.

I have two primary motivations here. The first is purely
maintainability. The current module structure is a mess, and the naming
is misleading when browsing the codebase for the first time.

The second motivation is to prepare for an upcoming rewrite of the
router queue. There will likely be two simultaneous implementations for
a while as the new one settles, so it's extra important the interface
into the router queue is well-defined. I want the fork between the two
worlds to be as clean as possible. This is a similar approach to the one
I took when rewriting the prefetching implementation.

Some of the key name changes:

- ppr-navigations.ts → render-tree.ts. This module's responsibility is
to create the tree that gets passed into React for rendering, currently
called the CacheNode tree. I considered renaming it to cache-node.ts,
but I kept it more generic because I have plans to refactor that type
soon.
- segment-cache/navigation.ts → app-router-state.ts. This module's
responsibility is to compute the next AppRouterState given inputs. It
does perform network requests, but it's conceptually a "pure" operation.
It doesn't deal with any of the router queueing logic. This is the most
reducer-y of the code that still exists, and along with render-tree.ts,
it will remain mostly shared between the old and new state machine
implementations.
- navigator.ts. This is a new module I created to act as the single
entry point for router-level operations: push, replace, navigate,
traverse, restore, refresh, hmrRefresh. This is the primary place where
we'll fork the old and new queue implementations. It's also how we'll
ensure the inactive implementation gets dead-code-eliminated properly
while the two implementations live side-by-side.
2026-08-15 23:46:33 -04:00
next-js-bot[bot] d1aeb1ceaf v16.3.1-canary.20 v16.3.1-canary.20 2026-08-15 23:21:56 +00:00
dan 2fbeebbaca Wait for back-before-hydration recoveries in the browser (#97321)
The `back-before-hydration` assertions read the heading with
`browser.elementByCss`, which waits for the load event before it
returns. Here the load event cannot fire until the stalled scripts are
released and finish running, and that is the whole window the test is
about. So the first attempt inside `retry` blocks for almost the entire
3s budget, looks at the DOM once, and then `retry` refuses a second look
because `waited + interval > duration`.

This waits in the browser instead, with the load event out of the
picture. Every page already has its own heading id, except the two
search pages, which shared one; they get a per-page id too. Then a
single `waitForSelector` resolves the moment that page commits, and the
URL assertion follows the wait rather than racing alongside it. The
assertions that check content *stayed* put keep polling with `retry`,
since a change there is the failure.

Holding the flight response for 1800ms and throttling the renderer 8x
reproduces the CI failure exactly on canary — same `Failed to retry
within 3000ms`, same expected "Home" / received "Post" — and passes with
this change; the suite is still 16/16 with the response held for 6s.

Test-only.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 09:03:50 -07:00
Josh Story 6d0379d4b8 Extract metadata resolution primitives (#97388)
## Summary

The existing metadata resolver combines two responsibilities: walking
the loader tree to collect and schedule route exports, and interpreting
the metadata or viewport value produced by an individual route layer.

Move the route-layer operations into metadata-resolution-primitives.ts.
This includes wrapping generateMetadata and generateViewport, loading
file-based metadata, merging values with their resolved parents,
post-processing metadata, and producing SelectedMetadata for rendering.

Keep resolve-metadata.ts responsible for the existing traversal and
scheduling flow, importing the extracted operations back into the same
call sites and preserving its existing exports. This creates a reusable
boundary for other traversal strategies without introducing another
resolution path.

This is intended to be behavior preserving: traversal order, eager
generator invocation, parent promise behavior, warnings, and rendered
metadata and viewport tags are unchanged.

## Verification

- pnpm build-all

<!-- NEXT_JS_LLM -->
2026-08-15 08:28:40 -07:00
Tim Neutkens b5538511ed test: update React 18 redbox snapshot (#97415) 2026-08-15 15:11:41 +02:00
Niklas Mischkulnig 904a41853a Turbopack: retain conditions when replacing resolve request keys (#97372)
Closes https://github.com/vercel/next.js/issues/97358

`ResolveResult::with_replaced_request_key` overwrote the `conditions` of
every result key with the conditions of the replacement key (empty at
all call sites). Those conditions are what distinguishes the results one
request resolved to under different export conditions, so both the
`module-sync` and the `default`/`require` target of the same subpath
ended up under the same `RequestKey`.

That is lossy as soon as a request resolves in more than one
`node_modules` directory: the results are merged through a
`RequestKey`-keyed map, so one of the two targets is silently dropped.
With a single candidate directory `ResolveResult::alternatives`
short-circuits and never builds that map, which is why this only showed
up in nested layouts.

In practice this broke `output: 'standalone'` with the Turbopack
production bundler for pnpm installs, whose virtual store hoists every
package into `node_modules/.pnpm/node_modules` and thereby creates that
second candidate directory: `next-server.js.nft.json` recorded only
`@swc/helpers/cjs/_interop_require_default.cjs`, while Node >= 22.12
resolves `@swc/helpers/_/_interop_require_default` to the `module-sync`
target `esm/_interop_require_default.js` (`@swc/helpers` 0.5.23 lists
`module-sync` first). The copied `.next/standalone/server.js` then
exited with MODULE_NOT_FOUND before listening.

---------

Co-authored-by: vercel-fleet[bot] <308483924+vercel-fleet[bot]@users.noreply.github.com>
2026-08-15 14:37:13 +02:00
Tim Neutkens 6b9001934d Remove server route matcher stack (#94157)
### What?

Remove the legacy server route matcher managers, route matcher
providers, and server route matcher classes. Move route definition and
params metadata into fsChecker output and request metadata instead.

### Why?

The filesystem checker already owns the normalized route lookup path in
production. Keeping a separate matcher manager stack duplicates route
inventory, adds reload plumbing, and makes dev and production behavior
harder to keep aligned.

### How?

- Build pages and app route definitions in fsChecker from production
manifests and from the dev route inventory.
- Thread matched route definitions and params through resolve-routes and
router-server request metadata into base-server and next-server.
- Remove reloadMatchers propagation and delete the legacy matcher
manager, provider, and matcher implementations and tests.
- Preserve dev ensurePage behavior, including legacy custom-server
render methods, and Pages API loading by using matched route definitions
while loading compiled dev modules where needed.

### Verification

- `pnpm test-start-webpack test/e2e/custom-routes/custom-routes.test.ts`
- `pnpm test-start-turbo test/e2e/custom-routes/custom-routes.test.ts`
- `pnpm test-dev-webpack test/e2e/custom-routes/custom-routes.test.ts`
- `pnpm test-dev-turbo test/e2e/custom-routes/custom-routes.test.ts`
- `pnpm test-dev-webpack
test/e2e/on-request-error/dynamic-routes/dynamic-routes.test.ts`
- `pnpm test-dev-turbo
test/e2e/on-request-error/dynamic-routes/dynamic-routes.test.ts`
- `pnpm test-start-webpack
test/e2e/app-dir/app-routes/app-custom-routes.test.ts`
- `pnpm test-start-webpack
test/e2e/app-dir/edge-route-rewrite/edge-route-rewrite.test.ts`
- `pnpm test-start-webpack
test/e2e/middleware-rewrites/test/index.test.ts`
- `pnpm test-start-webpack test/e2e/middleware-matcher/index.test.ts
test/e2e/middleware-custom-matchers-basepath/test/index.test.ts
test/e2e/app-dir/rewrites-redirects/rewrites-redirects.test.ts`
- `pnpm test-start-webpack test/e2e/api-catch-all/api-catch-all.test.ts
test/e2e/basepath-root-catch-all/basepath-root-catch-all.test.ts
test/production/root-catchall-cache/root-catchall-cache.test.ts`
- `pnpm test-start-webpack
test/e2e/i18n-beforefiles-rewrite/i18n-beforefiles-rewrite.test.ts`
- `pnpm test-start-webpack
test/e2e/basepath/redirect-and-rewrite.test.ts`
- `pnpm test-dev-webpack test/e2e/custom-server/custom-server.test.ts`
- `pnpm test-start-webpack test/e2e/custom-server/custom-server.test.ts`
- `pnpm --filter=next types`
- `pnpm --filter=next build`
- `git diff --check`

<!-- NEXT_JS_LLM_PR -->
2026-08-15 13:07:22 +02:00
next-js-bot[bot] 0d100b27d3 v16.3.1-canary.19 v16.3.1-canary.19 2026-08-14 23:22:15 +00:00
Josh Story ab8f484809 Adopt SelectedMetadata for metadata rendering (#97387)
The metadata resolver currently passes ResolvedMetadata directly to tag
rendering even though several fields only exist to carry state between
route layers.

Introduce SelectedMetadata as the post-processed, tag-ready
representation and convert the current resolved output before rendering.
This makes the final selection boundary explicit without changing
generated tags, preparing the metadata pipeline for multiple
independently resolved branches.

<!-- NEXT_JS_LLM -->
2026-08-14 16:04:19 -07:00
Steven 39e5570b9a fix(next/image): reject empty image on read/write to disk cache (#97278) 2026-08-14 17:49:49 -04:00
Marcos Hernanz ae0e0a5006 Turbopack: remove stale manifests for deleted routes (#97333)
## What?

- remove manifest fragments when Turbopack's development entrypoint
cleanup removes a deleted page
- add an end-to-end regression for replacing concrete App Router pages
with an optional catch-all without restarting the dev server

## Why?

Turbopack already removes deleted entry keys from its asset mapper,
subscriptions, issues, and client state. It did not remove the
corresponding records from `TurbopackManifestLoader`, so the next
manifest write continued to serialize deleted App Router routes.

When a deleted concrete route is replaced by an optional catch-all,
those stale records conflict with the new route at the same specificity.
The dev server reports the route conflict and responds with 404 until it
is restarted.

This is independently consistent with the cleanup identified in #97194.
This draft adds end-to-end coverage that fails on current canary by
observing both the 404 and stale manifest records, then passes with the
lifecycle fix.

## How?

Pass the existing manifest loader to `handleEntrypointsDevCleanup` and
call its existing `delete(key)` method in the same branch that deletes
the stale asset mapping. This keeps all per-entry manifest maps
synchronized with the authoritative current entrypoint set without
special-casing any route shape.

The regression test:

1. compiles two concrete localized routes
2. deletes both pages and adds an optional catch-all
3. waits for three affected paths to return the new catch-all content
4. verifies the development app-paths manifest contains the catch-all
and no deleted entries

## Verification

- reproduced the report on `16.3.1-canary.10`: all four reported paths
returned 404 after the live route replacement and returned 200 after a
restart
- confirmed the regression fails on current canary before the source
change with the same-specificity route error and a 404 response
- `pnpm test-dev-turbo
test/development/app-dir/hmr-deleted-page/hmr-deleted-page.test.ts`
- `pnpm test-dev-webpack
test/development/app-dir/hmr-deleted-page/hmr-deleted-page.test.ts`
- `pnpm test-dev-rspack
test/development/app-dir/hmr-deleted-page/hmr-deleted-page.test.ts`
- `CI=1 pnpm build-all`
- `pnpm --filter=next build`
- formatting, ESLint, staged-diff, and signed-commit checks

Fixes #97035
2026-08-14 14:17:29 -07:00
Niklas Mischkulnig 1173626d2e Turbopack: make unreachable codegen more generic (#97385)
Make the inserted comment configurable.
2026-08-14 22:51:41 +02:00