Commit Graph

3801 Commits

Author SHA1 Message Date
Benjamin Woodruff ‮ 7b58e5880c Turbopack: Add support for specifying additional roots (#98003)
Full motivation and plan here:
https://app.notion.com/p/vercel/Turbopack-pnpm-Global-Virtual-Store-383e06b059c480579403ddfd71cc2d40?source=copy_link

The goal is to allow `DiskFileSystem` to traverse outside of it's own
root to other configured `DiskFileSystem`s when following symlinks. We
may allow traversal in other situations in the future, but this is
limited to symlink resolution for now.

## Global Virtual Store

The motivation for this is to enable [pnpm's Global Virtual Store
feature](https://pnpm.io/global-virtual-store) (and there are other
package managers doing this, including nub and bun).

We'd expose the ability to manually configure this in `next.config.js`,
but we should also auto-configure ourselves for popular package managers
(or at least make a best effort to do so, the `PNPM_HOME` semantics can
be complicated). The `ignoreIfMissing` option is provided for this
situation: We can configure a bunch of roots automatically, and they
only actually get set up if they exist, the check for directory
existence is cheap.

## NFT changes

This requires a couple extensions to the `*.nft.json` file format:
https://github.com/vercel/next.js/pull/98469

## Related Issues

- #93556
- https://github.com/pnpm/pnpm/issues/14972
2026-09-18 17:08:40 -07:00
Aurora Scharff 41ef17c645 Add experimental agent feedback workflow (#98582)
## Summary

Adds an off-by-default `experimental.agentFeedback` workflow for
collecting Next.js friction without interrupting the user’s task.

- `next dev` writes a small managed block to the project
agent-instructions file.
- Agent entry points, including `next-dev-loop`, queue possible issues
instead of opening duplicate forms.
- At the final stopping point, an internal command checks the remote
gate and returns the reporting protocol bundled with that Next.js
version. The agent attempts to anonymize each qualifying issue and opens
a separate review form once. If the browser does not open, it prints the
URL for the user without troubleshooting the failure.
- Report links no longer contain a public page token. Nothing is sent
until the user submits the form, which remains rate-limited and uses a
server-only ingest credential.
- Disabling `agentFeedback` or `agentRules` removes only its managed
block on the next `next dev`. Empty generated agent files are cleaned
up; user-authored content is preserved.
- Adds API references for both options and updates the AI agents guide.

Bundling the protocol keeps the managed block small and allows the
report format to evolve with each Next.js version. The receiving form is
implemented in
[vercel/front#85739](https://github.com/vercel/front/pull/85739) and
should deploy before this workflow is enabled.

## Verification

- `NEXT_SKIP_ISOLATE=1 pnpm test-dev-turbo
test/development/app-dir/agent-rules-auto-generate/agent-rules-auto-generate.test.ts`
- `pnpm jest packages/next/src/server/lib/generate-agent-files.test.ts
packages/next/src/cli/internal`
- `npx eslint --config eslint.config.mjs
packages/create-next-app/helpers/generate-agent-files.ts
packages/next/src/server/config-shared.ts
packages/next/src/server/lib/generate-agent-files.ts
test/development/app-dir/agent-rules-auto-generate/agent-rules-auto-generate.test.ts`

<!-- NEXT_JS_LLM -->
2026-09-18 18:18:20 +02:00
Jiwon Choi 058456fac6 Set experimental.agenticAutoUpgrade after successful upgrade (#98871)
So the framework can nudge agent/human in the future
2026-09-18 15:39:50 +02:00
Jiwon Choi d3d30cf88a Add future defaults upgrade coverage for next upgrade --ai (#98643)
Stacked on #98640.

This PR adds `next upgrade --ai="future"` flag, which is targeted to
help users leverage agents to upgrade their app to adopt the future
defaults when available. Just like latest version upgrade, it covers
running codemods and a migration checklist for major-to-major upgrades
to support breaking changes more reliably.

This PR currently covers Cache Components only for the future default.
2026-09-18 00:52:21 +02:00
Jiwon Choi 97496b2078 Add "latest" upgrade coverage for next upgrade --ai (#98633)
Stacked on #98637.

This PR adds `next upgrade --ai="latest"` flag, which is targeted to
help users leverage agents to upgrade their app to the latest major
version when available. Just like security upgrade, it covers running
codemods and a migration checklist for major-to-major upgrades to
support breaking changes more reliably.
2026-09-18 00:52:21 +02:00
Jiwon Choi 9bb13254ae Nudge the agents for security vulnerable version upgrade (#98637)
Stacked on #98562.

> [!TIP]
> Recommended to review commit by commit.

This PR adds `experimental.agenticAutoUpgrade = 'security'` config which
enables nudging the agents to notify the user when the app's Next.js
version has any security advisories. The nudge will include guiding to
upgrade via `next upgrade --ai` (run "security" by detecting config).

The method of nudging leverages the agents behavior where they tend to
listen to messages from fatal errors that blocks the process compared to
general error/warning logs. Whenever the agents run `next dev` or `next
build`, Next.js will detect the condition and nudge the agent using this
method.

Afterwards it's up to the user whether to proceed the upgrade or not,
it's 100% up to the user how to run it e.g. subagent, background agent,
etc. and the process should not enforce any that affects user's
workflow.
2026-09-18 00:52:20 +02:00
Jiwon Choi 5d9ab72cef Add next upgrade --ai and security vulnerability coverage (#98562)
> [!TIP]
> Recommended to review commit by commit.

This PR adds `next upgrade --experimental-ai="security"` flag (alias
`--ai`), which is targeted to help users leverage agents to upgrade
their app to the safe major version when their app's Next.js version has
any security advisories.

Once the command is ran from the user, Next.js will detect the installed
agent harness in user's device, currently limited to Codex and Claude,
and will proceed with starting an agent session once approved. If it is
called within an agent session, the work will continue off within that
agent.

`next upgrade --ai` simply does two things:

- prepare the relevant context to temporary dir
- print hand off prompt, guiding to read those context

The context will guide the agent to run relevant codemods and migration
checklist to proceed. This PR is a base core of the workflow, and will
have wrappers of entry point around this. Also, will add "latest" and
"future" as follow up, which will cover the app to be always latest, and
adopt the future defaults like Cache Components.

This PR also sets up the evals infra and adds evals.
2026-09-18 00:52:20 +02:00
Joseph fbe0d56219 docs: rewrite the bots and crawlers section in the caching guide (#98782)
## Summary

The Bots and crawlers section in the caching guide said that all bots
and crawlers skip the static shell because "they need a complete
document". Only HTML-limited bots do, and the reason is metadata: with
Partial Prerendering, `generateMetadata` can resolve dynamically and
stream in after the shell, which puts the tags in the body rather than
the `<head>`. Bots that need them in the `<head>` get a dynamic render
instead.

The second paragraph now states the consequence directly: if the
build-time prerender read something unreachable at request time, the
render throws and those bots get a 500. Which user agents this covers,
and the `htmlLimitedBots` option for changing the list, stay owned by
the Streaming guide, which the section links to.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-09-17 13:17:59 +00:00
Joseph 6130a49316 docs: scope React cache() guidance to the render pass (#98741)
Reimpl of: https://github.com/vercel/next.js/pull/98579
2026-09-16 15:51:58 +00:00
Alex Kostyniuk 95d3d8ec81 docs: clarify metadata support in root not-found (#98670)
docs: document metadata/generateMetadata in root `app/not-found.js` as
it works, but docs don't specify it
2026-09-15 13:10:09 +02:00
Joseph e29a96a9ee docs: add an intro paragraph to the glossary (#98654)
The glossary page goes straight from frontmatter to the first entry, `#
A` —  the word "glossary" appears nowhere in its body.

We index docs for search from the rendered content, and the page title
is stored as non-searchable metadata, so searching "glossary" on
nextjs.org does not return the glossary.

This adds a two-sentence intro, which gives the page a body chunk
carrying its own name.

```mdx
The Next.js documentation uses a recurring set of terms for routing, rendering, and caching. This glossary defines them.
```

Also reads better than a page that opens directly to content.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Aurora Scharff <66901228+aurorascharff@users.noreply.github.com>
2026-09-15 08:41:17 +00:00
Joseph 25fe6f6f06 docs: document default turbopack.resolveExtensions (#98627)
Fixes: #98609

Editor note: Ensure this lands well into
https://github.com/vercel/next.js/pull/98407 too
2026-09-14 21:15:11 +02:00
Jiwon Choi 996f957c06 Document codemod errors and intentional migration exceptions (#98565)
Stacked on #98560.

### Why?

Users need to distinguish unfinished codemod repairs from intentional
feature-adoption opt-outs.

### How?

Document `@next-codemod-error` and temporary `UnsafeUnwrapped*` casts in
the codemod guide, with examples and a link to the Async Request APIs
migration guide. Explain when an inapplicable suggestion can be marked
`@next-codemod-ignore` with a reason.

Show the ignore comment emitted for Cache Components opt-outs and
explain that it does not block compilation.
2026-09-14 12:36:34 +02:00
Jimmy Miller 30d6d8f497 Adds this.mode for webpack loaders (#98532)
Pretty straightforward. But a number of loaders depend on it including

- postcss-loader
- sass-loader
- stylus-loader
- vue-loader
- nunjucks-loader
- thread-loader
2026-09-11 12:05:03 -07:00
Aurora Scharff 393de1ebe0 skill: add Partial Prefetching optimizer (#96471)
## Summary

- add a Partial Prefetching optimizer Skill for selected source Links
and destinations
- keep prefetch stages and Link policy in the public [Optimizing
prefetching
guide](https://nextjs.org/docs/app/guides/optimizing-prefetching), and
rely on the same static-shell documentation as the Cache Components
optimizer for cache placement, Suspense, loading UI, and authorization
- keep the Skill focused on the product decision, production `instant()`
loop, differential verification, and mutation freshness when an
optimization adds a cache around mutable data
- reference the canonical
[`unstable_prefetch()`](https://nextjs.org/docs/app/api-reference/functions/prefetch)
and
[`unstable_navigation()`](https://nextjs.org/docs/app/api-reference/functions/navigation)
docs instead of explaining the APIs again in the Skill
- link cache-writing optimizations to the canonical
[Revalidating](https://nextjs.org/docs/app/getting-started/caching-and-revalidating#revalidating)
documentation and verify a populated-cache mutation in the Skill and
eval
- bundle a self-contained production rig aligned with the related
adoption and optimizer Skills, tuned to record each source-link and
prefetch-stage contract
- add an eval that compares baseline, the bundled guide, and the local
Skill against the same prefetched-UI and cache-freshness goal
- document the optimizer in the AI agents guide

This follows the same guide/Skill split as #97737. Partial Prefetching
adoption preserves existing behavior; this optimizer is the later
decision about what is worth preparing before a click and what should
wait for navigation.

Related: #97712, #97737, #97759, #98122, #98175

## Eval

One run per treatment with Claude Opus 4.8 and Claude Sonnet 4.5, using
a fixed Claude Haiku 4.5 judge. The bundled-guide and Skill treatments
use a local Next.js tarball containing the new guide and API references.

| Model | Treatment | Criteria | Score change | Overall | Duration |
Tokens processed | Total run cost | Cost change |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| Opus 4.8 | Baseline | 10/10 (100%) | — | Pass | 18m 42s | 3.39M |
$3.46 | — |
| Opus 4.8 | Bundled guide | 9/10 (90%) | -10 pp | Fail | 13m 37s |
3.02M | $2.84 | -18.0% |
| Opus 4.8 | Local Skill | 10/10 (100%) | +10 pp | Pass | 17m 22s |
2.80M | $2.54 | -10.6% |
| Sonnet 4.5 | Baseline | 2/10 (20%) | — | Fail | 15m 55s | 1.97M |
$1.06 | — |
| Sonnet 4.5 | Bundled guide | 5/10 (50%) | +30 pp | Fail | 15m 49s |
2.17M | $1.11 | +4.5% |
| Sonnet 4.5 | Local Skill | 8/10 (80%) | +30 pp | Fail | 15m 31s |
1.41M | $0.77 | -30.4% |

Token totals count each unique model message once and include uncached
input, cache creation, cache reads, and output. Cache reads account for
most of the total in every treatment. Total run cost applies the current
AI Gateway base rates to each token category: Opus 4.8 costs $5/M input,
$6.25/M cache writes, $0.50/M cache reads, and $25/M output; Sonnet 4.5
costs $3/M input, $3.75/M cache writes, $0.30/M cache reads, and $15/M
output. The six agent runs cost $11.78 in total.

Compared with baseline, the Opus Skill run maintained a 100% criteria
pass rate while reducing cost by 26.7%. The Sonnet Skill run improved
the criteria pass rate from 20% to 80%, a 60 percentage-point increase,
while reducing cost by 27.3%.

The stronger model could solve the fixture without the Skill, although
it independently searched the bundled Next.js documentation and took
slightly longer than the Skill treatment. The Opus guide-only run
implemented the correct behavior but missed the required
RED-before-GREEN proof.

The Sonnet treatments show a clearer progression. Baseline did not
implement the required caching, navigation stage, `instant()` contract,
or cache invalidation. The bundled guide added the cached summary and
production test, but missed the navigation stage, deferred-state
assertions, tag invalidation, and RED-before-GREEN proof. The Skill
treatment completed the `instant()` RED-to-GREEN loop and final
production behavior, but left `connection()` in the related-sessions
read and used path revalidation instead of tagging the cached read. It
therefore passed 8 of 10 criteria but failed the overall eval.

All six measured runs completed through grading. Chromium and its system
dependencies are provisioned before the agent starts, so missing browser
libraries did not determine these results.

## Verification

- `pnpm --filter=next build`
- `pnpm prettier --check` for the changed guide, prompt, Skill, and eval
fixture
- `pnpm exec tsc -p
evals/evals/agent-051-optimize-partial-prefetching/tsconfig.json
--noEmit`
- `node --check run-evals.js`
- `git diff --check`
- packed a local Next.js tarball and confirmed it contains the optimizer
guide plus the `navigation()` and `prefetch()` references
- verified the `navigation()` contract in the minimal reproduction and
NextBeats on Next.js 16.4.0-canary.22
- merged the latest `canary`, preserving both generic fixture setup and
Playwright provisioning

<!-- NEXT_JS_LLM -->
2026-09-10 22:27:36 +02:00
Tobias Koppers ed1aab5d38 fix(turbopack): resolve ../ and /-rooted import.meta.glob patterns (#96557)
### What?

`import.meta.glob()` now resolves `../`-relative and
`/`-project-root-absolute
patterns (and `base`), validates every pattern (positive and negative)
for
whether it escapes the filesystem root, and shares the underlying
"resolve leading `./`/`../` against a directory" primitive with
`next-core::relativize_glob` (used by `outputFileTracingIncludes`)
instead of
duplicating it.

### Why?

`import.meta.glob()` handed its patterns straight to Turbopack's `Glob`,
which
matches relative to the scanned directory and understands neither a
leading
`/` nor `..` segments. `import.meta.glob('../../content/*.js')` and
`import.meta.glob('/content/*.js')` therefore silently returned `{}`,
and a
`base` that walked above the filesystem root failed with an internal
error
and a panic report instead of a normal diagnostic. A negative
(exclusion)
pattern that escaped the root was silently dropped, which is the worst
outcome for an exclusion — it would silently include files the user
asked to
exclude.

### How?

- Patterns are split into the directory they're rooted in and the
remaining
glob, matched relative to a single common scan directory covering every
  positive and negative pattern. `/` is resolved from the project root,
matching Vite's rule that patterns are relative to the importing file or
  absolute from the project root.
- Map keys follow the pattern too: `base` now produces base-relative
keys
  (`'./zeta.js'` instead of `'../../content/zeta.js'`, matching Vite), a
  project-root-absolute pattern produces project-root-absolute keys, and
  origin-relative keys are unchanged otherwise.
- Every pattern is validated up front; one that leaves the project (a
`base`,
a positive pattern, or a negative one) reports an error issue at the
call
  site and yields an empty object, instead of crashing or being silently
  dropped.
- The `./`/`../`-resolution loop is extracted into
  `turbo_tasks_fs::glob::relativize_glob`, a crate both `next-core` and
`turbopack-ecmascript` already depend on. `next-core::relativize_glob`
is
now a thin wrapper over it (same name, signature and error message); its
5
unit tests are removed since the shared primitive's own tests cover the
  same cases.

<!-- NEXT_JS_LLM -->

Co-authored-by: Niklas Mischkulnig
<4586894+mischnic@users.noreply.github.com>

---------

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-10 08:42:37 +02:00
Aurora Scharff 2aaa6e810a docs: preserve route behavior during Cache Components adoption (#98275)
## Summary

- Explain that removing `dynamic = 'force-dynamic'` moves a route from
full request-time rendering to Cache Components' Partial Prerendering
model, then document the choices for its request-time work.
- Preserve routes that previously used `dynamic = 'force-static'` or
`dynamic = 'error'` instead of leaving them under `instant = false`,
which can move rendering to request time and reduce prefetched UI.
- Clarify that existing `fetch` and `unstable_cache` caching can remain
in place during adoption.
- Combine the incremental checkpoint and completion coverage into one
eval that verifies both stages of the migration.
- Make the adoption skill honor an explicit incremental strategy before
continuing through a complete migration.
- Remove the unrelated `.env*` ignore and restore the existing agent-054
eval registration.

## Eval results

`agent-047-adopt-cache-components` runs the complete incremental
migration with `openai/gpt-5.6-luna` through the Codex harness. It
retains the original five checks for the first shippable checkpoint and
the original seven checks for completing the migration.

| Treatment | Result | Notes |
| --- | --- | --- |
| Baseline | 11/12 | Preserved the final behavior, but treated the work
as a fresh migration instead of explicitly continuing from the recorded
first checkpoint. |
| Guide (`AGENTS.md`) | 8/12 | Built successfully, but rewrote the
existing `unstable_cache`, changed the catalog's hourly cadence, moved
its timestamp to request time, and skipped the recorded incremental
checkpoint. |
| Adoption skill | 12/12 | Preserved both cache lifetimes and static
contracts, recorded a passing incremental checkpoint, removed the
remaining opt-outs, and completed the migration. |

## Verification

- `pnpm build`
- Luna eval runs for the baseline, guide, and adoption-skill treatments
- Prettier and ESLint checks for the changed guide, skill, and eval
files
- `git diff --check`

<!-- NEXT_JS_LLM -->
2026-09-09 23:04:34 +00:00
Aurora Scharff 526c492a60 docs: document React browser API (#97621)
### What?

- Documents React Canary's new `browser` API in the SPA guide
- Adds examples for marking a Client Component as browser-only and
wrapping it in Suspense

### Why?

The App Router includes React Canary releases, and the `browser` API now
provides a Suspense-based option for components that depend on browser
APIs such as `window` or `document`.

### How?

The guide calls `browser()` inside `use()` in a Client Component. It
then wraps that component in Suspense so prerendering can show the
closest fallback before the component renders in the browser.

### Testing

- `prettier --check docs/01-app/02-guides/single-page-applications.mdx`
- `alex docs/01-app/02-guides/single-page-applications.mdx --quiet`
(only existing SPA dictionary warnings)
2026-09-10 00:40:45 +02:00
Aurora Scharff 9486750ff2 [evals] Cover focused Cache Components and Partial Prefetching cases (#97813)
## Summary

- update existing evals for synchronous I/O, instant shell validation,
and Cache Components configuration instead of adding duplicate fixtures
- add focused Partial Prefetching coverage for URL-data inclusion, the
prefetch stage, and the navigation stage
- retain focused Cache Components coverage for session isolation and
URL-dependent shells
- rebase onto current `canary` so the Partial Prefetching evals can use
the public `unstable_prefetch()` and `unstable_navigation()` API
references

## Coverage

The latest results use Claude Sonnet 4.6 for the coding agent and Claude
Haiku 4.5 for the agentic judge. The Partial Prefetching cases have been
rerun after the new API references landed, and the corrected Cache
Components criteria have also been rerun.

| Eval | Scenario | Baseline | Bundled docs |
| --- | --- | ---: | ---: |
| `agent-035-connection-dynamic` | Adopt Cache Components while keeping
synchronous request-time values fresh and stable navigation in the shell
| 5/5 | 5/5 |
| `agent-040-instant` | Follow default validation and keep a stable
product title ahead of live inventory | 4/4 | 4/4 |
| `agent-042-enable-ppr` | Translate legacy route configuration while
preserving hourly and request-specific behavior | 4/4 | 4/4 |
| `agent-048-prefetch-url-data` | Include cached URL-specific category
content in a selected per-link prefetch | 3/6 | 6/6 |
| `agent-049-prefetch-stage` | Exclude cached content from the App Shell
but include it in a selected prefetch | 1/5 | 5/5 |
| `agent-051-prefetch-until-navigation` | Prefetch a selected title
while deferring the rest until navigation | 0/5 | 5/5 |
| `agent-052-cache-components-session-isolation` | Reuse shared data
without placing member-specific request data in a public cache | 5/5 |
5/5 |
| `agent-053-cache-components-url-shell` | Keep URL data below a useful
shell and include every output-changing value in the cache key | 3/5 |
5/5 |

After the public API references landed, the prefetch-stage and
navigation-stage evals show the intended documentation signal. Baseline
Sonnet passed 1/5 criteria for `unstable_prefetch()` and 0/5 for
`unstable_navigation()`. With the canonical bundled-docs instruction,
Sonnet found the new references and passed all 5/5 criteria in both
cases. After clarifying that the URL-data navigation must not block and
linking the general navigation docs to the optimizing-prefetching guide,
the URL-data case improved from 3/6 without docs to 6/6 with bundled
docs.

## Result interpretation

- `agent-048` now passes 6/6 with bundled docs. The scoped documentation
link led the agent to the URL-data prefetch pattern instead of a
blocking render. Baseline Sonnet added `prefetch={true}` but relied on
the previous full-payload behavior and missed both the Partial
Prefetching configuration and cached URL data, resulting in 3/6.
- The low baseline scores for `agent-049` and `agent-051` are the
intended signal. Without the new references, the agents did not discover
the prefetch and navigation stages. With bundled docs, both cases pass
5/5.
- `agent-053` is also an intended documentation signal: the baseline hid
the useful shell behind a broad fallback, while bundled docs preserved
the shell and passed 5/5.
- `agent-035` now passes 5/5 in both conditions after removing an
unnecessary requirement for visible loading UI in one small metadata
region. `agent-052` also passes 5/5 in both conditions after removing
two failures for an omitted explicit `cacheLife()`, because the default
cache profile still applies, and clarifying for the judge that an
uncached component reading `cookies()` or `headers()` remains
request-specific without another opt-out marker.

## Overlap decisions

- `agent-035` now carries the synchronous-I/O adoption case, so the
duplicate `agent-051` fixture was removed.
- `agent-042` now carries the route-configuration migration case, so the
duplicate `agent-048` fixture was removed.
- `agent-034` remains focused on correct async `cookies()` and
`headers()` usage. `agent-052` tests the separate risk of leaking
request-specific values through a public cache.
- `agent-040` tests one meaningful shell under default validation.
`agent-041` still tests independent streaming across several dashboard
sections.
- `agent-048` intentionally brings cached URL-specific content into a
selected prefetch. `agent-053` tests the opposite shared-shell
requirement during Cache Components adoption.

## Verification

- Dry fixture generation passes for all eight cases in the table.
- `pnpm build-all`
- `agent-049-prefetch-stage`: baseline 1/5, bundled docs 5/5
- `agent-051-prefetch-until-navigation`: baseline 0/5, bundled docs 5/5
- `agent-035-connection-dynamic`: baseline 5/5, bundled docs 5/5
- `agent-048-prefetch-url-data`: baseline 3/6, bundled docs 6/6
- `agent-052-cache-components-session-isolation`: baseline 5/5, bundled
docs 5/5
- `pnpm exec tsc -p
evals/evals/agent-035-connection-dynamic/tsconfig.json --noEmit`
- `pnpm exec tsc -p evals/evals/agent-042-enable-ppr/tsconfig.json
--noEmit`

<!-- NEXT_JS_LLM -->
2026-09-09 22:19:32 +02:00
Jimmy Miller b879776299 Adds this.target for webpack loaders (#98436)
There are a bunch of different loaders I found depending on this

- vue-loader
- svelte-loader
- svg-sprite-loader
- nunjucks-loader
- workerize-loader
- worklet-loader
- nativescript-worker-loader
- thread-loader
- svg-sprite-generation-loader

This doesn't by itself fix any of them fully, but does increase
compatibility
2026-09-09 12:46:24 -07:00
Aurora Scharff f8657de16c docs: clarify ISR with Suspense params (#98210)
## Summary

- warn that awaiting params inside Suspense without Partial Prefetching
prevents unlisted paths from using ISR
- clarify that route data may still be cached even though the
URL-specific route renders on each request
- link the migration warning to the ISR with Cache Components guide

## Why

This follows up on #98209. The migration eval showed that an agent can
preserve generateStaticParams but move params beneath Suspense while
Partial Prefetching is disabled. That produces a fallback shell, but
unlisted paths do not use ISR.

Context:
https://github.com/vercel/next.js/discussions/98177#discussioncomment-18252316

## Testing

- git diff --check
- Prettier check for both changed guides
- commit hooks: Prettier and ESLint
2026-09-09 14:34:25 +00:00
Joseph 26b2bbe024 example: Use a Redis cacheHandler w/o dependencies (#95346)
Verify locally:

- Copy the example to a local directory
- Install deps
- docker compose up -d
- npm run build && npm run start 
- navigate to localhost:3000/cet (or gmt)
- open the Redis view (link in localhost:3000/cet)
- revalidate data on /cet or /gmt
- verify it on the Redis view

I had to update the app to async params, updateTag, and write the
handler in such a way that it solved:

```
⨯ TypeError: p.segmentData.get is not a function
    at ignore-listed frames
⨯ Error: failed to pipe response
```

Also adding docs edits with this caveat.

An agent review, and cross checking with a community implementation,
surfaced, that we had to read tags from
`data.headers['x-next-cache-tags']`. This agent review added a few
comments, I think they are useful, but can cut down if needed.

Last but not least, had to update the Time API endpoint (former no
longer worked).
2026-09-09 14:42:25 +02:00
Aurora Scharff 552dd3538d docs: preserve Next.js development state (#98276)
## Summary

Explain in the Building guide that `.next/dev` contains the active
development server state and incremental compilation caches. Deleting or
moving `.next` makes the development server recreate that state, while a
separate `distDir` provides isolated production output.

Make the `next-dev-loop` Skill explicit that moving `.next` to a backup
is still a reset. Keep the AI coding agents guide focused on discovering
and inspecting the running development server.

Update the agentic regression eval so it leaves two working routes and
their development server running before introducing an invalid
`generateStaticParams` signature. The production error points into
`.next/types`, but the source contains the defect. The eval
independently checks that the agent fixes the source, preserves `.next`,
and verifies the result through the existing development session.

## Eval results

| Model | Treatment | Result | Source fix | Preserve `.next` | Existing
dev verification |
| --- | --- | --- | --- | --- | --- |
| GPT-5.6 Luna | Baseline | 1/3 | Fail | Fail | Pass |
| GPT-5.6 Luna | Bundled Guide via `AGENTS.md` | 2/3 | Pass | Fail |
Pass |
| GPT-5.6 Luna | `next-dev-loop` Skill | 3/3 | Pass | Pass | Pass |

The baseline tried to clear `.next`, moved it aside when deletion was
blocked, and did not leave a valid source fix. The Guide treatment read
the relevant `generateStaticParams` API reference and fixed the source,
but still moved `.next` because it did not retrieve the general Building
guide. The Skill treatment read the explicit preservation guidance, kept
the original `.next` directory and development server active, used a
separate `distDir` for the production build, and verified both routes
through the running app.

## Verification

- `pnpm --filter=next build`
- Agentic eval with GPT-5.6 Luna: baseline, bundled Guide, and
`next-dev-loop` Skill
- Prettier and ESLint checks on the changed files
- `git diff --check`

<!-- NEXT_JS_LLM -->
2026-09-08 23:54:37 +02:00
Jimmy Miller afb0dbac6a Adds .resolve to loaders api (#98361)
This fully fixes handlebars loader in my tests and a number of other
loaders depend on this as well. The implementation for getResolve is a
bit different in turbopack, but this should be semantically the same.
2026-09-08 13:02:16 -07:00
Joseph aab9387128 docs: document excluding content from a prefetch in the prefetching g… (#98175)
Guide introduction of unstable_prefetch and unstable_navigation.
2026-09-08 16:17:14 +02:00
Joseph 68e3fb1d68 docs: add unstable_navigation and unstable_prefetch API references (#98122)
Adds API reference pages for the two render-interruption functions, both
marked `version: experimental`.

- `unstable_navigation()`, which keeps a subtree out of a route's
prefetches so it renders on the navigation.
- `unstable_prefetch()`, which keeps a subtree out of a route's App
Shell so it renders on a per-link prefetch or on the navigation.

Each page opens with the App Shell, per-link prefetch, navigation
ladder, states which boundary its function moves content past, and
cross-references the sibling function and the Adopting Partial
Prefetching guide.
2026-09-08 15:46:47 +02:00
Joseph 6ef6e29db5 docs: Remove version label from Turbopack in Pages Router (#98316)
Remove the experimental flag from
https://nextjs.org/docs/pages/api-reference/config/next-config-js/turbopack
2026-09-07 11:43:50 +02:00
Joseph 602a2aba90 docs: add API reference pages for skipProxyUrlNormalize and skipTrailingSlashRedirect (#97967)
- Generated with the `/write-api-reference` skill
- These two options were only listed in
https://nextjs.org/docs/app/api-reference/file-conventions/proxy#advanced-proxy-flags

Closes: https://github.com/vercel/next.js/issues/56090
2026-09-04 15:43:03 +02:00
Joseph 4e2abec31d docs: note that experimental.taint taints process.env (#98145)
Enabling `experimental.taint` makes Next.js taint `process.env` for you
(`app-render.tsx`), so passing it whole to a Client Component throws.
The docs never mentioned this.

Adds it to the existing "Good to know" callout, plus a caveat that the
taint is by object reference, so `process.env.MY_VAR` and `{
...process.env }` still pass through.
2026-09-03 12:10:36 +02:00
Aurora Scharff 87a1c18eee test: preserve static params during Cache Components migration (#98209)
## Summary

Adds an agent eval covering migration of the legacy on-demand ISR
pattern: `force-static`, `revalidate`, and an empty
`generateStaticParams` result.

The assertions require the agent to enable Cache Components, preserve
the `generateStaticParams` export, and replace its empty result with at
least one parameter instead of deleting the function. The fixture also
runs the Cache Components adoption skill with the established 30-minute
timeout.

Updates the Cache Components migration guide to link each legacy route
segment config to its migration instructions. The `generateStaticParams`
section now explains that removing the export opts a dynamic route out
of ISR and links to the ISR with Cache Components guide.

## Eval results

### Before the guide change

| Treatment | Result | Duration | Behavior |
| --- | --- | ---: | --- |
| Baseline | Passed (1/1) | 482.9s | Preserved `generateStaticParams`
and returned a real seed. |
| Bundled docs / AGENTS.md | Failed (0/1) | 444.7s | Deleted
`generateStaticParams`, incorrectly treating omission as equivalent to
returning an empty array. |
| Cache Components skill | Passed (1/1) | 527.6s | Preserved
`generateStaticParams` and returned a real seed. |

The failed treatment was classified as a model failure. It read the
bundled migration guide but concluded that deleting
`generateStaticParams` preserved the defer-all-paths-to-runtime
behavior. The eval catches that loss of on-demand ISR semantics.

The full run used the identical fixture immediately before its numeric
identifier was changed from `agent-044` to the final `agent-054` name.
The fixture and assertions were unchanged.

### After the guide change

| Treatment | Result | Duration | Behavior |
| --- | --- | ---: | --- |
| Baseline | Passed (1/1) | 739.5s | Preserved `generateStaticParams`,
returned `FEATURED_EVENT_SLUG`, and compared the migrated route with the
original behavior. |
| Bundled docs / AGENTS.md | Passed (1/1) | 393.2s | Read the revised
migration and ISR guides, preserved `generateStaticParams`, and returned
`FEATURED_EVENT_SLUG`. |
| Cache Components skill | Passed (1/1) | 547.3s | Preserved
`generateStaticParams`, returned `FEATURED_EVENT_SLUG`, and verified the
build and representative routes in development. |

In the first controlled pair, the bundled-docs treatment changed from
failing to passing. Its final migration kept the route eligible for ISR,
preserved the 60-second revalidation behavior with `use cache` and
`cacheLife`, and produced a passing build.

### Additional variance check

The bundled-docs treatment was run once more against each guide version:

| Guide version | First sample | Second sample | Observed result |
| --- | --- | --- | --- |
| Before this change | Failed (444.7s) | Passed (455.6s) | 1/2 passed |
| After this change | Passed (393.2s) | Passed (425.2s) | 2/2 passed |

The second pre-change sample shows that the previous wording could still
lead the agent to the correct migration. The revised wording passed both
observed samples, suggesting that the explicit ISR consequence improves
reliability. Two samples per version are not enough to claim a stable
pass rate.

## Verification

- `pnpm --filter=next build`
- `pnpm eval agent-054-cache-components-empty-static-params --dry`
- `pnpm eval agent-054-cache-components-empty-static-params`

<!-- NEXT_JS_LLM -->
2026-09-03 11:57:49 +02:00
Tim Neutkens fb4c498852 Upgrade web-vitals to v6 and report soft navigations (#97232)
## Summary

Upgrades the vendored `web-vitals` dependency to v6.0.1 and enables
soft-navigation reporting for CLS, INP, and LCP. This lets
`useReportWebVitals` report metrics against the navigation where they
occurred, including `navigationURL` for route attribution.

The upgrade also removes the obsolete FID integration and compatibility
patch, regenerates the standard and attribution bundles, and updates the
public metric types and documentation to match the current upstream API.

Related to #97161.

## Verification

- Added production-mode coverage for initial and soft-navigation Web
Vitals reporting with both Turbopack and Webpack.

<!-- NEXT_JS_LLM -->
2026-09-03 09:45:19 +02:00
Benjamin Woodruff ‮ 5cca033c41 docs: local development: Rewrite docker section, add Windows Dev Drive and WSL2 instructions (#98164)
Follow-up to
https://vercel.slack.com/archives/C046HAU4H7F/p1787344658655399

Summary:
- Move system configuration changes into it's own section, separate from
application and project configuration changes.
- The Docker section was very light on details and didn't do a good job
of explaining why this problem happens.
- Also suggest
https://docs.docker.com/desktop/features/synchronized-file-sharing/ for
Docker and explicitly discourage `watchOptions.pollIntervalMs` as a
workaround.
- Suggest Windows Dev Drive
(https://learn.microsoft.com/en-us/windows/dev-drive/). I don't have
experience with it, but Microsoft recommends it and claims it improves
filesystem performance.
- Recommend trying WSL 2.
2026-09-02 08:49:10 -07:00
Aurora Scharff 1a9245c7d0 docs: document preloading with Cache Components (#97864)
## Summary

- add the preload pattern to the current App Router data fetching guide
- distinguish automatic fetch memoization, React cache for non-fetch
data, and Cache Functions
- clarify request-scoped private caching and when stale: Infinity should
not constrain the route stale time

The previous-model caching guide remains unchanged.

## Verification

- Prettier on both edited docs
- alex on both edited docs
- git diff --check
- Not run: full framework build because this is a documentation-only
change

<!-- NEXT_JS_LLM -->
2026-09-01 13:03:19 +02:00
Aurora Scharff e9180eae2a errors: shorten "use cache" messages and unify them into one factory (#94300)
### What?

Centralizes related <code>"use cache"</code> scope errors and rewrites
them as shorter, actionable messages with consistent <code>Learn
more:</code> links. It also updates the reachable revalidation errors
for rendering and <code>generateStaticParams</code>.

### Why?

The previous messages were long, inconsistent, and sometimes missing
documentation links. Some new cache-specific revalidation messages were
also hidden by an earlier render-phase error. The new messages name the
constraint, give the immediate fix, and link to the relevant
documentation.

### How?

Adds centralized error factories and dedicated error pages for request
data, cache configuration, private-cache composition, and revalidation.
When an active App Router route is available, the messages include it.
The error pages use consistent terminology and complete examples. The
<code>unstable_cache()</code> reference now documents the
request-dependent operations that these errors reject.

### Before and after

Route-based examples use <code>/products</code>. Bracketed values are
alternatives in matching order. For example, <code>[E1482, E1486,
E1489]</code> maps to <code>[headers(), cookies(), request.url]</code>.
Each runtime error contains one alternative, but the table groups
messages with the same template.

#### Messages that include the route

| Cases | Before | After |
| --- | --- | --- |
| E1480 · <code>searchParams</code> in <code>"use cache"</code> | Route
/products used <code>searchParams</code> inside "use cache". Accessing
dynamic request data inside a cache scope is not supported. If you need
some search params inside a cached function await
<code>searchParams</code> outside of the cached function and pass only
the required search params as arguments to the cached function. See more
info here: https://nextjs.org/docs/messages/next-request-in-use-cache |
Route "/products": <code>searchParams</code> can't be read inside
<code>"use cache"</code>. Await it outside the cached function and pass
what you need as an argument.<br><br>Learn more:
https://nextjs.org/docs/messages/next-request-in-use-cache |
| [E1482, E1486, E1489] · [<code>headers()</code>,
<code>cookies()</code>, <code>request.url</code>] in <code>"use
cache"</code> | Route /products used [<code>headers()</code>,
<code>cookies()</code>, <code>request.url</code>] inside "use cache".
Accessing Dynamic data sources inside a cache scope is not supported. If
you need this data inside a cached function use [<code>headers()</code>,
<code>cookies()</code>, <code>request.url</code>] outside of the cached
function and pass the required dynamic data in as an argument. See more
info here: https://nextjs.org/docs/messages/next-request-in-use-cache |
Route "/products": [<code>headers()</code>, <code>cookies()</code>,
<code>request.url</code>] can't be read inside <code>"use cache"</code>.
Read it outside the cached function and pass what you need as an
argument.<br><br>Learn more:
https://nextjs.org/docs/messages/next-request-in-use-cache |
| [E1481, E1485, E1492] · [<code>cookies()</code>,
<code>request.url</code>, <code>headers()</code>] in
<code>unstable_cache()</code> | Route /products used
[<code>cookies()</code>, <code>request.url</code>,
<code>headers()</code>] inside a function cached with
<code>unstable_cache()</code>. Accessing Dynamic data sources inside a
cache scope is not supported. If you need this data inside a cached
function use [<code>cookies()</code>, <code>request.url</code>,
<code>headers()</code>] outside of the cached function and pass the
required dynamic data in as an argument. See more info here:
https://nextjs.org/docs/app/api-reference/functions/unstable_cache |
Route "/products": [<code>cookies()</code>, <code>request.url</code>,
<code>headers()</code>] can't be read inside
<code>unstable_cache()</code>. Read it outside the cached function and
pass what you need as an argument.<br><br>Learn more:
https://nextjs.org/docs/app/api-reference/functions/unstable_cache |
| [E1484, E1491] · <code>draftMode().enable()</code> in
[<code>unstable_cache()</code>, <code>"use cache"</code>] | Route
/products used "draftMode().enable()" inside [a function cached with
<code>unstable_cache()</code>, "use cache"]. The enabled status of
<code>draftMode()</code> can be read in caches but you must not enable
or disable <code>draftMode()</code> inside a cache. See more info here:
[https://nextjs.org/docs/app/api-reference/functions/unstable_cache,
https://nextjs.org/docs/messages/next-request-in-use-cache] | Route
"/products": <code>draftMode().enable()</code> can't be called inside
[<code>unstable_cache()</code>, <code>"use cache"</code>]. Draft mode
can be read inside a cached function, but enabling or disabling it must
happen outside.<br><br>Learn more:
[https://nextjs.org/docs/app/api-reference/functions/unstable_cache,
https://nextjs.org/docs/messages/next-request-in-use-cache] |
| [E1488, E1499] · <code>connection()</code> in
[<code>unstable_cache()</code>, <code>"use cache"</code>] | Route
/products used <code>connection()</code> inside [a function cached with
<code>unstable_cache()</code>, "use cache"]. The
<code>connection()</code> function is used to indicate the subsequent
code must only run when there is an actual request, but caches must be
able to be produced before a request, so this function is not allowed in
this scope. See more info here:
[https://nextjs.org/docs/app/api-reference/functions/unstable_cache,
https://nextjs.org/docs/messages/next-request-in-use-cache] | Route
"/products": <code>connection()</code> can't be called inside
[<code>unstable_cache()</code>, <code>"use cache"</code>] because cached
functions may run during prerendering, without an incoming request. Call
it outside the cached function.<br><br>Learn more:
[https://nextjs.org/docs/app/api-reference/functions/unstable_cache,
https://nextjs.org/docs/messages/next-request-in-use-cache] |
| E1494 · <code>connection()</code> in <code>"use cache: private"</code>
| Route /products used <code>connection()</code> inside "use cache:
private". The <code>connection()</code> function is used to indicate the
subsequent code must only run when there is an actual navigation
request, but caches must be able to be produced before a navigation
request, so this function is not allowed in this scope. See more info
here: https://nextjs.org/docs/messages/next-request-in-use-cache | Route
"/products": <code>connection()</code> can't be called inside <code>"use
cache: private"</code> because private cached functions may run during
prefetching, without a navigation request. Call it outside the cached
function.<br><br>Learn more:
https://nextjs.org/docs/app/api-reference/directives/use-cache-private |
| [E1483, E1495] · <code>revalidateTag("products")</code> in [<code>"use
cache"</code>, <code>unstable_cache()</code>] | Route /products used
"revalidateTag products" inside [a "use cache", a function cached with
"unstable_cache(...)"] which is unsupported. To ensure revalidation is
performed consistently it must always happen outside of renders and
cached functions. See more info here:
https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering
| Route "/products": <code>revalidateTag("products")</code> can't be
called during render, inside a cached function, or inside
<code>generateStaticParams</code>. Call it from a Server Action or Route
Handler instead.<br><br>Learn more:
https://nextjs.org/docs/messages/revalidate-in-use-cache |
| During render | Route /products used "revalidateTag products" during
render which is unsupported. To ensure revalidation is performed
consistently it must always happen outside of renders and cached
functions. See more info here:
https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering
| Route "/products": <code>revalidateTag("products")</code> can't be
called during render, inside a cached function, or inside
<code>generateStaticParams</code>. Call it from a Server Action or Route
Handler instead.<br><br>Learn more:
https://nextjs.org/docs/messages/revalidate-in-use-cache |
| Inside <code>generateStaticParams</code> | Route /products used
"revalidateTag products" inside <code>generateStaticParams</code> which
is unsupported. To ensure revalidation is performed consistently it must
always happen outside of renders and cached functions. See more info
here:
https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering
| Route "/products": <code>revalidateTag("products")</code> can't be
called inside <code>generateStaticParams</code>. Call it from a Server
Action or Route Handler instead.<br><br>Learn more:
https://nextjs.org/docs/messages/revalidate-in-use-cache |

#### Other messages

| Cases | Before | After |
| --- | --- | --- |
| [E1477, E1490] · Nested cache with [short <code>expire</code>,
<code>revalidate: 0</code>] | A "use cache" with [short
<code>expire</code> (under 5 minutes), zero <code>revalidate</code>] is
nested inside another "use cache" that has no explicit
<code>cacheLife</code>, which is not allowed during prerendering. Add
<code>cacheLife()</code> to the outer "use cache" to choose whether it
should be prerendered [with longer <code>expire</code>, with non-zero
<code>revalidate</code>] or remain dynamic [with short
<code>expire</code>, with zero <code>revalidate</code>]. Read more:
https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife
| Route <code>"/products"</code>: A nested <code>"use cache"</code> with
[a short <code>expire</code> (under 5 minutes), <code>revalidate:
0</code>] is inside an outer <code>"use cache"</code> that has no
<code>cacheLife()</code>. Add <code>cacheLife()</code> to the outer one
to choose whether to prerender it [with a longer <code>expire</code>,
with a non-zero <code>revalidate</code>] or keep it dynamic [with a
short <code>expire</code>, with <code>revalidate:
0</code>].<br><br>Learn more:
https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife
|
| E1478 · External promise | Filling a "use cache" entry appears to be
stuck on shared state from the outer render scope. The same function
completed when run in isolation, which usually means a module-scoped
value (for example a top-level Map used to dedupe fetches) is joining a
promise created outside the cache. "use cache" already dedupes calls
with the same arguments within a request and across requests on the same
server instance, so the surrounding dedupe layer is both unnecessary and
the likely cause. Remove it and rely on "use cache" alone for
deduping.<br><br><em>No documentation link.</em> | Route
<code>"/products"</code>: A <code>"use cache"</code> function is
awaiting a promise created outside it. The same call completed when run
in isolation, so a module-scoped value (often a top-level
<code>Map</code> used to dedupe fetches) is most likely blocking it.
<code>"use cache"</code> already dedupes calls with the same arguments.
Remove the surrounding dedupe layer.<br><br>Learn more:
https://nextjs.org/docs/messages/next-request-in-use-cache |
| [E1479, E1498] · [<code>cacheTag()</code>, <code>cacheLife()</code>]
outside a cached function | [<code>cacheTag()</code>,
<code>cacheLife()</code>] can only be called inside a "use cache"
function.<br><br><em>No documentation link.</em> | Route
<code>"/products"</code>: [<code>cacheTag()</code>,
<code>cacheLife()</code>] can only be called inside a <code>"use
cache"</code> or <code>"use cache: private"</code>
function.<br><br>Learn more:
[https://nextjs.org/docs/messages/cache-tag-outside-use-cache,
https://nextjs.org/docs/messages/cache-life-outside-use-cache]<br><br>Outside
an App Router route, the same message is shown without the route prefix.
|
| E1487 · Prerender timeout | Filling a cache during prerender timed
out, likely because request-specific arguments such as params,
searchParams, cookies() or dynamic data were used inside "use
cache".<br><br><em>No documentation link.</em> | Route
<code>"/products"</code>: A <code>"use cache"</code> function took too
long during prerendering. The most common cause is passing unresolved
request-specific arguments, such as <code>params</code> or
<code>searchParams</code>, into the cached function. Resolve the data
before calling the function and pass only the values you
need.<br><br>Learn more:
https://nextjs.org/docs/messages/next-request-in-use-cache |
| E1493 · Private cache inside a public cache | "use cache: private"
must not be used within "use cache". It can only be nested inside of
another "use cache: private".<br><br><em>No documentation link.</em> |
Route <code>"/products"</code>: <code>"use cache: private"</code> can't
be nested inside <code>"use cache"</code> because a shared cached
function can't depend on private request data. Nest it only inside
another <code>"use cache: private"</code>.<br><br>Learn more:
https://nextjs.org/docs/messages/use-cache-private-composition |
| E1496 · Private cache without a request | "use cache: private" cannot
be used outside of a request context.<br><br><em>No documentation
link.</em> | Route <code>"/products"</code>: <code>"use cache:
private"</code> needs an active request, so it can't be used during
<code>generateStaticParams</code> or other build-time contexts. Move it
to a request-time component or function.<br><br>Learn more:
https://nextjs.org/docs/messages/use-cache-private-composition |
| E1497 · Private cache inside <code>unstable_cache()</code> | "use
cache: private" must not be used within
<code>unstable_cache()</code>.<br><br><em>No documentation link.</em> |
Route <code>"/products"</code>: <code>"use cache: private"</code> can't
be used inside <code>unstable_cache()</code> because
<code>unstable_cache()</code> uses a shared cache that can't contain
private request data. Call the private cached function outside
<code>unstable_cache()</code>.<br><br>Learn more:
https://nextjs.org/docs/messages/use-cache-private-composition |

### Runtime verification

- Exercised the 13 route-based message IDs through minimal dev-runtime
reproductions. Twelve reached the new factories from userland, with
byte-identical output across three requests each.
- The `request.url` error for `"use cache"` Route Handlers is currently
limited to the prerender path. A dynamic request captured by a cached
closure can bypass that tracking; this is existing framework behavior to
follow up separately.
- Passing the `searchParams` promise into a nested cached function can
still surface the earlier synchronous dynamic-API error before this
factory. The new message is verified when the cache scope reads its own
`searchParams` value.
- A follow-up preview verification triggered 17 of the 19 rewritten
messages from userland with byte-identical output. The timeout-driven
external-promise and prerender-timeout messages were verified in source
and through their focused test coverage.

### Verification

- <code>CI=1 pnpm build-all</code>
- <code>pnpm --filter=next types</code>
- <code>pnpm --filter=next build</code>
- <code>HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts</code>
- <code>HEADLESS=true pnpm test-start-turbo
test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts</code>
- <code>NEXT_SKIP_ISOLATE=1 HEADLESS=true pnpm test-dev-webpack
test/e2e/app-dir/cache-components-errors/use-cache.test.ts -t 'cacheLife
with (expire &lt; 5 minutes|revalidate: 0)'</code> (6 tests and 6
snapshots passed)
- <code>pnpm test-dev
test/e2e/app-dir/cache-components-errors/use-cache.test.ts --projects
jest.config.*</code> (42 tests and 42 snapshots passed across Turbopack
and webpack)
- <code>pnpm test-dev
test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts --projects
jest.config.*</code> (10 tests and 8 snapshots passed across Turbopack
and webpack)
- <code>pnpm test-dev
test/e2e/app-dir/use-cache-configured-timeout/use-cache-configured-timeout.test.ts
--projects jest.config.*</code> (4 tests and 2 snapshots passed across
Turbopack and webpack)
- Prettier, ESLint, and Alex on the changed source, tests, and error
pages

<!-- NEXT_JS_LLM -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 00:29:56 +02:00
Joseph b48cb74271 docs: document create-next-app Cache Components prompt and flag (#97798)
Follow up to: Add Cache Components option to create-next-app - #97695
2026-08-31 19:06:39 +02:00
Hendrik Liebau 00b2275c26 docs: Fix the documented contract for custom cache handlers (#98039)
The documented in-memory handler stored the resolved entry and returned
it from every `get`. A stored stream keeps the request that produced the
entry reachable. The second cache hit also fails, because it reads a
stream that another reader already consumed. The render then reports
"Invalid state: ReadableStream is locked".

The "Handling Streams" section described a second, milder mistake. It
told a handler to tee the stored stream in `get`. One branch goes back
into the store, and the other serves the current read. That pattern
serves every hit correctly. The built-in handler used it until #97941,
so the page described the framework's own implementation. The stored
branch retains the same request, and it also holds the source stream and
its buffered chunks. The size a store accounts for covers none of that.
The page now states that cost, because a reader who thinks of teeing
would otherwise read the rule as inapplicable.

`get` told a handler to drop an entry once it is past `revalidate`.
Next.js compares `timestamp` against `expire` on every read, and it
treats a too-old entry as a miss. It applies the same check to
`revalidate` when the result goes into another server cache. A handler
therefore needs no age check at all. The built-in handler still drops at
`revalidate`, as a deliberate policy for an in-memory cache, and the
page now presents that as a policy instead of a rule.

Something still has to reclaim entries, so the page states that eviction
belongs to the handler. Next.js never deletes from it. An age check at
serve time is no substitute, because it only reaches the keys that a
reader still asks for.

The page now follows an order in which no rule appears after the code
that depends on it. The stream rules sit in one section ahead of the API
reference. `CacheEntry Type` moves above the methods that reference it.
`set` precedes `get`, because a handler writes an entry before it reads
one. The tag and error sections move above the examples.

`types.ts` described `refreshTags` as periodic, and the page described
it as a call before each request. Neither is accurate. It runs once per
request, before the first cache read for its kind. A request that reads
nothing from a handler never calls it.

Neither `types.ts` nor the page documented the `revalidate: -1` signal,
which the built-in handler relies on. A negative value always lies in
the past, so it serves an entry with a stale tag one more time while
Next.js generates a fresh one.

`types.ts` now states these rules as well, so a handler author no longer
has to read the built-in implementation to find them. It also covers the
`pendingEntry` promise, which a handler must not retain either.

`streamFromBuffer` now records why it has to stay a default stream. A
byte stream transfers the buffer of every chunk it receives. That would
detach the buffer an in-memory handler serves many reads from. The
comment in `tiered-cache-handler.ts` about cancelling a teed stream now
says that the backing handler is user-configured, because the built-in
one no longer returns such a branch.

The handler test from #97941 now records a limit of its own. It only
fails on Node 20 and 22, whose `AsyncLocalStorage` attaches the active
store to every promise. CI runs Node 20.9, so a regression fails there.
A local run on a newer Node passes.

Nothing in this change affects runtime behavior.
2026-08-31 13:09:07 +02:00
Aurora Scharff 086294e2e5 docs: add layout stability guidance for videos and iframes (#98070)
## Summary

- Document how dimensions and poster images keep video layouts stable
while media loads.
- Add equivalent sizing guidance for lazy-loaded iframes.

## Verification

- `prettier --check docs/01-app/02-guides/videos.mdx`

<!-- NEXT_JS_LLM_PR -->
2026-08-31 11:22:47 +02:00
Tobias Koppers a84bc8de2a Turbopack: mangle exported names for smaller bundle sizes (#97672)
### What?

Adds export-name mangling to Turbopack, behind a new experimental option
`experimental.turbopackMangleExportNames` (default `false`). It is
independent of minification:
`--no-mangling` is a minifier flag and does not affect it.

When enabled, each ECMAScript module's *used* export names — including
`default` and
`__esModule` — are replaced by short keys in the emitted output, both
where the module registers
its exports and where every consumer reads them. Modules whose export
names could be observed by
user code keep their original names, decided per module.

This is a reland of #89060 (on top of the already-merged #89406),
originally written by Matt
Mastracci, who is credited as a co-author on the commit.

Stacked on top: #97676 flips the default to `true` on canary releases,
so Next.js's own CI
exercises the feature broadly before it is considered for stable.

### Why?

Bundle size. A module's export keys exist only to link modules together:
the producing module
emits `{ someVeryLongExportName: … }` and every consumer reads
`ns["someVeryLongExportName"]`.
Both sides are generated by us, so as long as producer and consumer
agree — and the name isn't
observable from user code — the key can be a single character. Long
export names are extremely
common in real dependency graphs (icon sets, utility packages, barrel
files), and each one is paid
for once in the module that defines it and once per importing module.

### How?

**Ported, not rebased.** `canary` is ~2500 commits past the original
stack's base, and the files it
touched were independently rewritten in the meantime (export-analysis
refactor #92781, CJS analysis
for scope hoisting #95826, the `module_fragments` subsystem #95978). A
probe rebase produced 16
conflicting files on the first commit alone, so the original branches
were used as a reference
implementation — for intent, the identifier alphabet, and test coverage
— and the feature was
rebuilt on today's infrastructure. #89561 from the original stack
(erasing the Next.js wrapper
module types) is deliberately **not** part of this change; it turned out
to be unnecessary, because
those modules already declare whole-module export usage and therefore
back off on their own.

**The name table** (`references/esm/mangle/table.rs`) hashes each name
into a table of all valid JS
identifiers of the smallest length that fits the name set — 15 exports
get single-character keys —
and resolves collisions by open addressing. Hashing rather than
assigning `a`, `b`, `c`, … is what
keeps names stable: an unrelated edit elsewhere in the module doesn't
renumber every other export,
and a collision only perturbs its own cluster. Assignment happens in two
passes: every name that is
*already* a valid identifier at the chosen length keeps itself and
reserves its bucket first, and
only then is anything hashed — so an export called `a` keeps `a`, and
nothing else can be assigned
it. Both passes iterate in sorted order, so the mapping depends only on
the set of names.

A module with exactly **one** mangleable export is special-cased to a
fixed key, `f`, rather than a
hashed one. `f` is the most common character in JS keywords (`if`,
`for`, `function`), and every
single-export module in the graph then emits the same `.f` / `.f()` byte
sequences, which gzip's
back-references pick up across the whole bundle — a bigger win than
hashing, at the cost of that one
key changing when a second export is added.

A fixed list, `RESERVED_KEYS`, is withheld from every table for two
different reasons: JS reserved
words (`if`, `in`, `do`, `for`, `let`, `new`, `try`, `var`) are legal as
quoted property keys but a
minifier will not fold `ns["if"]` into the shorter `ns.if`, so handing
one out costs bytes instead of
saving them; and `__esModule` is withheld because the runtime's `esm()`
helper defines that property
on every module's exports object regardless of what the module itself
exports, so an assigned key
landing on it would collide. (`default` needs no such protection — once
it is mangled like any other
export, nothing else emits a property under that literal name.)

**One source of truth for the mapping.** `mangled_export_names(module,
chunking_context)` is a
turbo-task that both the producing side (`EsmExports::code_generation`)
and the consuming side
(`ReferencedAssetIdent::Module`, the single place a cross-module export
access is materialized) ask
for the *target* module's map. Neither side computes a table of its own,
so they cannot disagree,
and the task derives export usage from the chunking context itself
rather than accepting it as an
argument, so a caller can't supply usage from the wrong graph. Re-export
chains need no special
handling, because the consumer side already resolves through re-exports
to the module that produces
the binding.

The mangling decision itself lives on `EsmExports` as a
`mangle_export_names: bool` field, rather
than a separate trait method every module type has to override. A module
that derives its exports
from another one (a facade, a locals module, a part, a rename) inherits
the flag with the data,
which removed seven hand-written delegations and the possibility of a
new wrapper type forgetting
one.

**A mangling decision must not cross module identities.** A few module
types hand out *another*
module's exports value as their own (the WASM loader module, the
module-fragments side-effects
wrapper, the client-reference proxy). If that borrowed value carried a
real mangling decision, the
producing and consuming sides would key their lookups on two different
modules and could compute two
different keys for the same export — this actually broke every WASM- and
`@vercel/og`-based test
once the default-on layer exercised it in CI.
`EcmascriptExports::borrowed()` is the one place this
is handled: it always returns an unmangled view, and every such
pass-through site uses it.

**Back-off is per module**, built on the export-usage information that
landed after the original PR
(`BindingUsageInfo` / `ModuleExportUsageInfo`) rather than the
original's locals/facade-split
heuristic. A module keeps its names when its usage is `All` (a namespace
import that couldn't be
lowered, a computed property access, an unresolvable `export *`, or a
chunk-group entry — which
covers client references and the Next.js wrapper modules), when it is
read through a namespace value
at all, when its exports are dynamic or not statically known ESM, or
when names aren't being mangled
in this build.

`__webpack_exports_info__` gains `canMangle` and `mangledName` per
export, which is how a running
test can observe the mapping; with the option off it emits exactly what
it emitted before.

### Testing

- `turbo-tasks-hash`/table unit tests: encode/decode round-trip,
degenerate-name rejection, table
sizing, the single-export fixed key (including its own reservation),
reserved-word withholding
(including a reserved bucket-count test that stays in sync with the
reserved list), the
preserved-name pass running before any hashing, uniqueness under heavy
collision, order
  independence, wrap-around probing, and same-tier stability.
- 13 `turbopack-tests` execution fixtures under
`tests/execution/turbopack/exports/mangle-*`,
several ported from the original PR and from webpack's
`test/configCases/mangle`: named imports,
re-export chains and default exports (including one literally named
`__esModule`), escaping
namespaces (`Object.keys`, `delete ns.missing`, `export * as`, CJS
interop), destructuring,
prototype-shadowing names (`toString`, `$1`, `__1`), a 60-export
two-character table, dynamic
`import()` with `webpackExports` / `turbopackExports`, a CommonJS
consumer of an ESM module,
dynamic re-exports, scope hoisting on and off, and a control with the
option off.
- 3 committed snapshot fixtures under `tests/snapshot/mangle-exports`,
so the emitted keys, the
  back-off, and the fixed single-export key are visible in review.
- One fixture under `__skipped__`, which the harness asserts *fails*,
recording the
  namespace-materialization gap below.
- Full suite: 546 unit + 272 execution + 125 snapshot tests pass, with
no snapshot churn across the
  several refactors this PR went through in review.
- Verified against real builds: targeted app-dir, worker, WASM, and
`@vercel/og`-based e2e suites
pass with the option forced on (the failures that remain are
external-network tests that fail
identically with it off), and a small two-page app shrank by 0.76% of
total emitted JS / 0.53%
  gzipped.

### Known limitations, each intentional

- **A module read through `import * as ns` is never mangled**, even when
every read is statically
tracked, because the analysis doesn't yet distinguish a lowered named
read from a materialized
namespace object. Namespace imports are common, so this leaves real wins
on the table; unlocking
  it is the highest-value follow-up.
- **Escaping namespaces back off entirely.** Webpack instead keeps
mangling and materializes a
namespace object keyed by the original names. That is the `__skipped__`
fixture: implementing it
  turns the suite red until the fixture is moved out.
- **CommonJS export mangling is out of scope.** The producing side is
reachable, but the consuming
accesses live in user source and nothing rewrites them today; it needs
its own design pass.

Closes PACK-435

<!-- NEXT_JS_LLM -->


Co-authored-by: Luke Sandberg
<210140+lukesandberg@users.noreply.github.com>


<!-- fleet ecdfa248-cd54-41ac-b4a2-c9d49e2a67ee -->

---------

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>
2026-08-28 07:46:16 +02:00
Aurora Scharff 24ed66abe9 docs(skills): preserve prefetched UI during Partial Prefetching adoption (#97712)
## What

- Documents how legacy `prefetch={true}` behavior changes with Partial
Prefetching and how to choose the UI worth preserving.
- Updates the adoption skill to capture a passing flag-off `instant()`
baseline, use the same assertions to drive the migration, and retain
them as regression coverage.
- Adds a reusable production test rig, keeps additional per-link
optimization separate from adoption, and surfaces `prefetch={false}`
links that may no longer be needed.
- Applies the shared test-rig fixes discovered during dogfooding to the
Cache Components optimizer.

## Why

Legacy `prefetch={true}` could include uncached dynamic content. With
Partial Prefetching, it includes the shared App Shell and cached
URL-specific content instead. A passing build or clean insight sweep
does not prove that the intended prefetched UI survived the migration,
so the skill now records that contract before adoption and verifies it
unchanged afterward. The guide presents the same test workflow as an
optional programmatic check for manual adopters.

## Testing

- Prettier, Alex, and skill frontmatter validation
- Dogfooded on v0 in https://github.com/vercel/v0/pull/28842
- Partial Prefetching adoption eval: 6/6 in
https://github.com/vercel/next.js/pull/97759
2026-08-27 19:46:19 +00:00
Rich Haines e80a85bcff docs: improve discovery summaries (#97982)
This PR improves the meta descriptions on certain pages.

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

We encourage you to use AI to assist you in researching, creating, and
reviewing changes. However, you must review and deeply understand the
contributions you are making. For this reason, **pull request
descriptions from external contributors must be written by a human**.

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Signed commits

- This repository requires verified commit signatures on protected
branches.
- If this pull request is blocked for unsigned commits, re-sign the
commits and force-push the branch.
- A `Signed-off-by` line in the commit message is not enough.

## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->
2026-08-27 19:47:39 +02:00
Joseph efd592e9d0 docs: explain origin matching for allowedOrigins and allowedDevOrigins (#97805)
The `allowedDevOrigins`, and `allowedOrigins` options, behave slightly
different to each other, the former drops the PORT, and it is not clear
where are these picked up from. This PR improves guidance on how to use
these two.
2026-08-26 10:29:42 +00:00
Joseph c51f582a70 docs: clarify revalidateTag profile expire semantics (#97836)
Once you call `revalidateTag`, the next request that reads the tagged
data kicks off revalidation.

That tagged data is SWR until the revalidation completes, or until the
expire time, whichever comes first.

It is the profile's expire time, counted from the `revalidateTag` call,
that defines the limit after which requests block.

If the first request after `revalidateTag` is called arrives after the
expire time, it'd block.

So the profile is a way to say, after this tag is revalidated, past the
expire time, data correctness is more important than being fast.
2026-08-25 12:46:13 +02:00
Joseph 03a09695be docs: param access on use-server (#97865)
Correctness pass — following
https://github.com/vercel/next.js/pull/97823
2026-08-25 12:45:54 +02:00
Tobias Koppers 3d57c6e581 docs: fix typos and correctness issues in App Router docs (#97823)
## Summary

A systematic review pass over the App Router documentation
(`docs/01-app`, 281 MDX files) fixing clear-cut typos, grammar issues,
and technical inaccuracies — code samples, file names, and API
signatures that had drifted from the implementation in
`packages/next/src`.

The review ran four passes: an automated spell check with a triaged
allowlist, targeted grammar-pattern searches, structural checks
(frontmatter, code fences, link targets), and a technical-accuracy pass
over all getting-started pages plus high-traffic API reference pages,
with every suspected inaccuracy verified against the source before
editing. Only unambiguous issues were fixed; anything debatable was
deliberately left alone (e.g. the `/aboot` route in the typedRoutes docs
is an *intentional* invalid-route example, and the CLI docs table
intentionally mirrors the actual `--help` output).

Notable fixes:

- JSX examples now `await params`, matching the Promise-based API and
their TypeScript counterparts
- `generateStaticParams()` is now exported in samples so the convention
actually takes effect
- `<Link prefetch>` props table includes `"auto"`, matching the `boolean
| "auto" | null` type in `link.tsx`
- `Navigator.geolocation` casing, the canonical react.dev `"use client"`
link, and a missing `Link` import in the typedRoutes docs
- Mismatched or wrong `filename=` labels in TS/JS switcher pairs
(`form.mdx`, `mutating-data.mdx`, `css.mdx`) and a wrong
`generateStaticParams` anchor target
- ESLint monorepo example registers the plugin as `@next/next` so the
documented rule prefixes resolve
- Spelling/grammar: `emphaize`, `invokations`, `behaviour` → American
spelling, "setup" → "set up" as a verb (5×), "a RSC" → "an RSC",
`NextJS` → `Next.js`, a stray comma, and "a `opengraph-image.tsx`"
article plus its folder path

## Verification

- `cspell` over all 281 files in `docs/01-app`: 0 issues outside a
reviewed allowlist of product names, API identifiers, and intentional
example strings
- `prettier --check` (3.6.2, repo config): passes on all 19 changed
files
- Grammar-pattern and structure greps: clean
- Technical claims cross-checked against `packages/next/src` and
`packages/eslint-plugin-next`
- Docs-only change; no runtime code touched

<!-- NEXT_JS_LLM -->


<!-- fleet eeae4385-18f5-406d-915e-986961503ed2 -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-08-25 12:12:37 +02:00
Jiwon Choi 9480f67d61 Document catchError for the Pages Router (#97748)
Supersedes https://github.com/vercel/next.js/pull/91848

`catchError` works in both the App Router and Pages Router, but its API
reference only documented the App Router behavior. This adds a Pages
Router reference backed by the same canonical document and scopes the
content that differs between routers.

The Pages Router examples use `reset()` because `retry()` is App
Router-only, omit App Router-only navigation behavior, and use Pages
Router component paths. The existing App Router guidance and shared API
reference remain unchanged.

<!-- NEXT_JS_LLM -->
2026-08-24 20:19:34 +02:00
Joseph 30e73d2d14 docs: note that response headers must be set before calling handle() in a custom server (#97693)
Fixes: #82387

A custom server that calls `res.setHeader()` after `await handle(req,
res)` silently loses the header, because Next.js has already begun
sending the response by the time `handle` resolves. Unlike a normal Node
`setHeader()` after `headersSent`, this does not throw, so the header
just never reaches the client. This adds a note to the custom server
guide showing the correct ordering and pointing out that the same
applies to framework wrappers such as `reply.raw` in Fastify.
2026-08-24 15:41:23 +02:00
MB c0dfeffa53 Add next/cache-handlers types entrypoint (#97592)
### What?

Exposes the `cacheHandlers` types (`CacheHandler`, `CacheEntry`) as
types-only exports from `next/cache`, so handlers can be checked against
the real interface:

```ts
import type { CacheHandler } from 'next/cache'
```

### Why?

Custom cache handler authors currently import `CacheHandler` and
`CacheEntry` from `next/dist/server/lib/cache-handlers/types` (an
internal path that can move between versions) or hand-copy the
interfaces, which drift silently across releases.

### Motivation

I'm working on a custom community cache handler and it'd be great to
import the types directly and keep testing against the source of truth,
rather than maintaining a hand-copied mirror that has to be re-checked
on every Next.js release.

Raised in discussion #96356.

### How?

- Adds a type-only `export type { CacheHandler, CacheEntry }` to
`packages/next/cache.d.ts`, re-exported from
`./dist/server/lib/cache-handlers/types`. No new subpath or
`package.json` `"files"` entries needed since `next/cache` already
ships.
- Docs: `cacheHandlers.mdx` now shows the public `next/cache` import
instead of the GitHub source links.
- Tests: a `satisfies CacheHandler` / `satisfies CacheEntry` fixture in
the `typescript-basic` typechecking suite (runs `tsc` against the
installed package), and the `use-cache-custom-handler` e2e fixture's
JSDoc now uses the public import.

(Originally proposed as a separate `next/cache-handlers` types-only
subpath, following the `next/types` pattern moved the export into
`next/cache` per review.)

Related: #96356

closes #97781 (only created to run deploy tests)
2026-08-24 13:14:31 +02:00
Eddy (Frontend Engineer) 24f975677e docs(adapters): document assetsHashes and routing.middlewareMatchers (#96536)
While writing an adapter against `16.3.0-canary.107` I noticed the
adapter docs are missing two fields that the shipped `NextAdapter` types
already have.

**`assetsHashes`** — every `PAGES` / `PAGES_API` / `APP_PAGE` /
`APP_ROUTE` / `MIDDLEWARE` output carries it right next to `assets`, and
it's declared with a doc comment in `build-complete.d.ts`, but none of
the five shapes in Output Types mention it.

**`routing.middlewareMatchers`** — dumping the `routing` object from a
real `onBuildComplete` call gives:

```
afterFiles, beforeFiles, beforeMiddleware, dynamicRoutes, fallback,
middlewareMatchers, onMatch, rsc, shouldNormalizeNextData
```

but the docs list eight of those nine, everywhere the interface appears:
the Creating an Adapter snippet (which says "The interface is defined as
follows"), the API Reference parameter list, and Routing Information.
This one feels worth fixing soon — an adapter that does its own request
matching from the documented fields alone has no way to decide when
middleware should run. My guess for why nobody has hit it: if you pass
`routes: routing` wholesale into `resolveRoutes` from `@next/routing`,
everything works without ever looking at the field.

The wording I added comes from the doc comments in the shipped types,
not my own descriptions. `middlewareMatchers` is inserted where the type
puts it (right after `beforeMiddleware`).

For what it's worth: I checked the rest of the section against the same
build while I was at it — the `output: 'export'` behavior, the prerender
classification fields, `pprChain.headers`, the fallback fields, the
immutable-assets flow, and the `@next/routing` params/result — and
everything else matched the docs. These two were the only gaps I found.
2026-08-24 11:37:53 +02:00
Marcos Hernanz e694c08b6f docs: fix parallelRouteKey parameter name in useSelectedLayoutSegment(s) (#97710)
The `useSelectedLayoutSegment` and `useSelectedLayoutSegments` hooks
accept a
parameter named `parallelRouteKey` (singular), as declared in
`packages/next/src/client/components/navigation.ts`:

export function useSelectedLayoutSegment(parallelRouteKey: string =
'children')

The docs wrote it as `parallelRoutesKey` (plural) in three files, which
doesn't
match the generated `navigation.d.ts` type that editors surface via
IntelliSense:

- `use-selected-layout-segment.mdx` — parameter signature and
description
- `use-selected-layout-segments.mdx` — parameter signature and
description
- `parallel-routes.mdx` — "With useSelectedLayoutSegment(s)" section

Docs-only change; no behavior change.

---

Credit: This contribution was originally authored by @see-stack in
#97690 and is recreated here with signed commits.

Co-authored-by: Said Nasser <sdnssr001@gmail.com>
2026-08-21 16:05:03 -07:00
Joseph b2c48352d6 docs: correct the upper stale bound for App Shell exclusion in cacheLife (#97653)
Clarifying that:

> - **`stale` from 30 seconds up to 5 minutes**: included in prerenders,
but excluded from the route's [App Shell](/docs/app/glossary#app-shell).

Means, 30 <= stale and stale < 5minutes
2026-08-21 14:46:44 +02:00