Commit Graph

3777 Commits

Author SHA1 Message Date
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
Joseph a91dff6e9e docs: Adapters entry page lists its children pages twice (#97677)
Both https://nextjs.org/docs/app/api-reference/adapters, and
https://nextjs.org/docs/pages/api-reference/adapters lists their
children twice, once from the PR content, and another from next-site's
rendering engine. Let's keep the next-site UI, as it lists each entry as
a card with title + description.
2026-08-21 11:56:54 +00:00
Joseph 18766e9071 docs: clarify beforeInteractive placement for root layouts (#97643)
Fixes: https://github.com/vercel/next.js/issues/97602
2026-08-21 13:47:48 +02:00
Joseph f5bdf1ba33 docs: document deploymentId build ID override and Pages Router skew in 16.2 (#97645)
- Addressing: https://github.com/vercel/next.js/issues/97632
2026-08-20 23:46:40 +02:00
Joseph 9b2387d23d docs: Explicit cache output description (#97548)
Completing the input-output pair for cached functions.

This is addressing user confusion around what happens to a cached
function, and functions within the scope.
2026-08-20 15:26:38 +02:00
Janka Uryga c10b89598a [PPF] Remove unstable_eager (#97524)
The implementation of `unstable_eager` is currently incomplete, causes
bugs in the main Partial Prefetching flow (see #97469), and is generally
not worth the complexity it adds, so we're removing it. We may add
something like this back later if we think it's useful.

Importantly, removing it fixes a bug that caused apps that use
incremental opt-in (i.e. no global `partialPrefetching` config and
`export const prefetch = 'partial'`) to issue speculative prefetches for
all links without `prefetch={true}`, because any segment without an
explicit `prefetch = "partial"` was marked as needing eager prefetching,
which was then propagated to the whole route. See #97469 for a full
explanation.

This fix causes a lot of changes in tests, which were previously
incorrectly asserting that we should be seeing two requests. Now that
the unnecesary speculative prefetch is gone, most of them only see one.
2026-08-19 23:21:57 +02:00
Jiwon Choi 1e6423eae7 docs: generateMetadata values should be serializable with use cache (#97551)
### Why?

generateMetadata can be marked with use cache, but Cache Function return
values must be serializable. The metadataBase examples use URL objects,
so the interaction needs co-located guidance for cached metadata.

### How?

Add a Good to know note that recommends returning metadataBase as a
string, such as with url.toString(), and links to the use cache
serialization requirements. Runtime behavior is unchanged.

<!-- NEXT_JS_LLM -->

---------

Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
2026-08-19 16:37:53 +00:00
Aurora Scharff 508085526d docs: adjust interactive app guide (#97558)
## What?

Updates the Interactive Apps guide to show how an optimistic mutation
handles an expected server error.

## Why?

The optimistic card move automatically returns to the confirmed data
when the write fails, but the guide did not show how to explain that
failure to the user. A success toast would duplicate the visible card
movement, so the example only adds feedback for the error case.

## How?

- Return a structured error result when the task no longer exists.
- Display that expected error in a toast while `useOptimistic` reverts
the card.
- Forward unexpected thrown errors to the nearest error boundary.
2026-08-19 15:36:19 +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
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
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
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
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
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
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
Hendrik Liebau 40188b5e39 [test] Cover 'use cache: private' in route handlers (#97349)
The `use-cache-private` suite covered private caches in server
components only, so this adds a route handler that calls one `'use
cache: private'` function three times: a concurrent pair, and a third
call after a short delay. The concurrent pair joins a single in-flight
invocation, while the delayed call lands after the map of pending
intra-request invocations has dropped its entry, so it is served from
the completed entry that the request retains. Each call passes a fresh
object literal, which makes the React `cache()` memo miss on reference
equality so that the lookup falls through to the serialized cache key.
The cache function also reads a cookie, which covers that request APIs
are allowed inside a private cache in a route handler.

A second test asserts that nothing is shared across requests. In
production private entries are neither persisted nor deduped across
requests, so every request re-runs the cache function. In development
they are persisted and forced to `revalidate: 0`, so the next request is
served the stale entry while a fresh one warms in the background, which
the dev branch of the test follows. Both tests pass their own `id`
search param, and therefore use their own cache key, so the entry that
dev persists for one is never served to the other.

This also drops the claim that the directive is not available in route
handlers from the `'use cache: private'` reference. Nothing in the
source rejects it, and it was already allowed for request work unit
stores when that note was written.
2026-08-14 10:50:45 +02:00
Aurora Scharff 34af419794 docs: align SPA guide reducer example with React conventions (#97341) 2026-08-14 09:55:27 +02:00
Marcos Hernanz 5785f98586 Update authentication diagram URL (#97320)
### What?

Updates the light and dark image references in the authentication guide
to use `authentication-overview-new.png`.

### Why?

The previous diagrams still showed `useFormState()` even though the
documentation has migrated to `useActionState()`.

Fixes #75709

### How?

Points both themed diagram references at the corrected replacement
assets.

Validation:

- Confirmed both replacement URLs return `200 image/png`
- `git diff --check`
2026-08-13 12:17:37 -07:00
Sam Poder 2cd4f0a730 [turbopack] Document clusters for chunking (#97267)
Adds docs for `clusters`.
2026-08-12 15:17:50 -07:00
Joseph bfc922eba6 docs: server client and directives (#96145)
- Enrich the directives entry point
- New guide for Server and Client components. Conceptual backing for the
getting started page.

---------

Co-authored-by: Aurora Scharff <aurora.sofie@gmail.com>
2026-08-11 16:07:03 +00:00
Aurora Scharff bbec4fe2bd docs: clarify client cache freshness after hydration (#97180)
## Summary

- add an explicit `staleTime` to the server-seeded TanStack Query
example so hydration does not immediately refetch
- explain when stale TanStack Query data becomes eligible to refetch
- clarify that SWR fallback data revalidates after hydration by default
and that `revalidateIfStale: false` is not a timed freshness window

## Verification

- `prettier --write
docs/01-app/02-guides/client-side-data-fetching/{swr,tanstack-query}.mdx`
- `alex
docs/01-app/02-guides/client-side-data-fetching/{swr,tanstack-query}.mdx
--quiet`
- Not run: full `pnpm lint` (docs-only change; targeted formatting and
language checks passed)

<!-- NEXT_JS_LLM -->
2026-08-11 17:09:47 +02:00
Aurora Scharff 7cb68c1282 docs: create client-side fetching guide (#96341)
## What changed

- Expands the SPA data-fetching guidance with focused SWR and TanStack
Query guides.
- Covers client-only fetching, optional Server Component seeding, shared
cache identities, and optimistic mutations.
- Explains how the Next.js server and client caches relate to
library-owned browser caches, including SWR `preload` and TanStack Query
hydration with Cache Components.
- Aligns the examples with `next-spa-patterns` and the team chat demo.

## Validation

- Prettier and diff checks pass for the updated guides.
- `next-spa-patterns` passes TypeScript checks.
2026-08-11 11:42:48 +00:00
kelvin 7b848a4e2b Turbopack: accept a module type argument for import.meta.glob (#96991)
### What?

Adds support for passing a module type to the TypeScript overloads for
`import.meta.glob`:

```ts
const entries = import.meta.glob<Entry>("./entries/*.ts");
```

This works with both lazy and eager imports. If a module type isn’t
provided, it defaults to `unknown`, which matches the current behavior.

### Why?

Next.js already supports Vite’s `import.meta.glob` API at runtime, but
its TypeScript declarations don’t support [Vite’s module
generic](https://github.com/vitejs/vite/blob/57fea001d154e7dd8d5d74d3082731f1dcfd31be/packages/vite/types/importGlob.d.ts#L113-L130).

Because of this, a typed glob works in Vite but fails in Next.js:

```text
TS2558: Expected 0 type arguments, but got 1.
```

Consumers currently have to cast the result or add their own
`ImportMeta` declaration to describe the matched modules. Supporting the
generic removes the need for those workarounds and makes typed glob
imports consistent between Next.js and Vite.

### How?

Each existing overload now accepts a generic module type, `M`, which
defaults to `unknown`:

- Lazy imports return `Record<string, () => Promise<M>>`.
- Eager imports return `Record<string, M>`.

Using `unknown` as the default means existing calls without a generic
keep the same types as before.

I also updated the existing e2e fixture. It had an old local
`ImportMeta.glob` declaration that returned `Record<string, any>`.
Removing that declaration exposed the missing generic and allowed the
five `as any` casts in the fixture to be removed. Without the generic,
all five glob calls fail during `next build` with `TS2558`. With it, all
five pass.

The Turbopack documentation has also been updated with examples showing
how to use the generic with lazy and eager imports.

### Checklist

- [x] `pnpm --filter=next types` passes
- [x] `pnpm prettier-fix` run
- [x] Documentation follows the docs contribution guide
- [x] E2E test passes

---------

Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
2026-08-11 12:05:56 +02:00
Aurora Scharff 0324a9ae0a docs: align Cache Components authentication guide title (#97143)
## Summary

Rename the guide to `How to implement authentication with Cache
Components` so it follows the wording of the main authentication guide
while keeping the Cache Components context.

## Verification

- `pnpm prettier --with-node-modules --ignore-path .prettierignore
--write docs/01-app/02-guides/authentication-with-cache-components.mdx`
- Not run: full test suite (frontmatter-only documentation change)

<!-- NEXT_JS_LLM -->
2026-08-11 11:30:41 +02:00
Marcos Hernanz b2e7958242 docs: fix Link prefetch grammar and Client Components wording (#97132)
## Summary

- Fix wording in Link prefetch docs: `navigation's` -> `navigation`
- Fix wording in CRA migration guide: `clients components` -> `Client
Components`

## Why

These are user-facing documentation wording issues. This change improves
clarity and readability for readers following these guides.

## Scope

Docs-only changes. No runtime behavior changes.

Attribution: This change was originally authored by @Rezakarimzadeh98 in
#96998.

Co-authored-by: Reza Karimzadeh <r.karimzadeh1998@gmail.com>
2026-08-10 15:48:46 -07:00