Added an important exception (for when client components wrap server
components) under an original line that seems to claim a universal law.
<!-- 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
### 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 #
-->
---------
Co-authored-by: Joseph <sephxd1234@gmail.com>
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
Note: re-apply of #92817
### What?
Deprecates the Edge Runtime and the `preferredRegion` route segment
config that only applied to edge runtime with build-time warnings,
TypeScript IDE hints, error documentation pages, and documentation
updates.
### Why?
Edge Runtime in Next.js is being deprecated in favor of the Node.js
runtime (the default). The `preferredRegion` route segment config is
tightly coupled to Edge Runtime and is also being deprecated.
### How?
**Edge Runtime deprecation:**
- Added `warnAboutEdgeRuntime()` helper using `Log.warnOnce` in
`packages/next/src/build/warn-about-edge-runtime.ts`
- Warning fires in `get-page-static-info.ts` (Webpack dev/build +
Turbopack build) and `turbopack-utils.ts` (Turbopack dev) when `runtime
= 'edge'` is detected
- Marked `'edge'` as `@deprecated` in the TypeScript language service
plugin (`rules/config.ts`)
- Created `errors/edge-runtime-deprecated.mdx` with migration guidance
- Updated documentation across 11 files to add deprecation notices or
simplify/remove outdated Edge Runtime references
**`preferredRegion` deprecation:**
- Added `warnAboutPreferredRegion()` helper in the same file
- Warning fires in `get-page-static-info.ts` for both App Router
(`config.preferredRegion`) and Pages Router (`config.config?.regions`)
- Marked `preferredRegion` as `@deprecated` in the TypeScript language
service plugin and the generated types (`next-types-plugin`)
- Created `errors/preferred-region-deprecated.mdx` with migration
guidance (remove the export)
- Updated documentation across 4 files to add deprecation notices
**Tests:**
- Added `test/e2e/edge-runtime-deprecated/` with fixture and assertions
for the edge runtime warning (dev + production)
- Added `test/production/preferred-region-deprecated/` with fixture and
assertions for the preferredRegion warning (production only -- Turbopack
dev does not surface `preferredRegion` from the Rust layer to JS)
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
This flag has been enabled by default on canary for some time. This
turns it on by default to ensure that CDNs that don't respect `Vary`
properly distinguish between different response types.
### What?
Adds `experimental.turbopackWorkerAssetPrefix` — a Turbopack counterpart
to webpack's
[`output.workerPublicPath`](https://webpack.js.org/configuration/output/#outputworkerpublicpath).
Opt-in, off by default, fully backward compatible.
```js
// next.config.js
module.exports = {
assetPrefix: 'https://cdn.example.com',
experimental: {
// Same shape as `assetPrefix`: bare prefix, no trailing slash, no `/_next`.
// `/_next/` is appended automatically. `''` → same-origin `/_next/...`.
turbopackWorkerAssetPrefix: '',
},
}
```
### Why?
When `assetPrefix` points to a cross-origin CDN (common production
setup), Turbopack emits Worker URLs under that CDN origin. Browsers
reject cross-origin `new Worker(url)` with `SecurityError`, breaking
every feature that uses `new Worker(new URL('./worker.ts',
import.meta.url))` — image decoders, WebP/GIF renderers, compression,
OffscreenCanvas, etc.
webpack solved this with `output.workerPublicPath`. Turbopack ignores
the `webpack()` callback in `next.config.js`, so the existing
same-origin workaround silently breaks when projects switch to
Turbopack.
Full design doc, alternatives considered, related issues (#88602,
#92676, #74621, #29468): **#93044**
Repro repo (`pnpm dev` reproduces, `pnpm dev:webpack` works):
https://github.com/GuinsooRocky/next-turbopack-worker-cdn-repro
### How?
Threads a new optional `turbopack_worker_asset_prefix: Option<RcStr>`
from `ExperimentalConfig` → `BrowserChunkingContext` → the JS runtime,
where `createWorker` reads it via a new `WORKER_BASE_PATH` global
emitted alongside the existing `CHUNK_BASE_PATH`. When unset, behavior
is identical to today.
Semantics, modeled on `assetPrefix`:
- **Bare prefix.** `/_next/` is appended automatically (same as
`computed_asset_prefix`); the user supplies just the origin (or empty
string for same-origin).
- **`undefined` vs `''`.** `undefined` falls back to the regular chunk
base path; `''` is a literal empty prefix that resolves to same-origin
`/_next/...`. The runtime distinguishes the two by injecting a JS `null`
literal vs a quoted string, and uses `WORKER_BASE_PATH ??
CHUNK_BASE_PATH` (not `||`).
- **Applies to entrypoint and module chunks.** The Turbopack worker
bootstrap rejects cross-origin module chunks (`Refusing to load script
from foreign origin`), so the override has to cover both — overriding
only the entrypoint would leave the worker unable to load any chunks
under a cross-origin `assetPrefix`.
Touchpoints:
- `packages/next/src/server/config-{shared,schema}.ts` — public option +
zod under `experimental`
- `crates/next-{api,core}/...` — wiring through to
`ClientChunkingContextOptions`;
`NextConfig::turbopack_worker_asset_prefix()` resolves to
`Some("<prefix>/_next/")` or `None`
- `turbopack/crates/turbopack-browser/src/chunking_context.rs` — new
field + builder + getter
- `turbopack/crates/turbopack-ecmascript-runtime/src/browser_runtime.rs`
— emit `WORKER_BASE_PATH` declaration as `null` or a quoted string
- `turbopack/.../runtime-base.ts` — `createWorker` resolves
`workerBasePath` and uses it for both the entrypoint URL and
module-chunk URLs delivered via params
- `test/e2e/turbopack-worker-asset-prefix/` — e2e fixture using real
cross-origin (`localhost` page, `127.0.0.1` `assetPrefix`); intercepts
`new Worker()` to assert on the URL the runtime helper resolved. Three
cases: no override (cross-origin URL → `SecurityError`), explicit
override origin, and `''` (relative `/_next/...`).
- `docs/01-app/03-api-reference/08-turbopack.mdx` — option reference
- `telemetry/events/version.ts` — `useTurbopackWorkerAssetPrefix:
boolean` (set on any explicit value, including empty string)
### Notes
- Opening as **Draft** to invite API-shape feedback before going to
review. Per the contributing guide, the design discussion is at #93044.
- All checklist items the PR template requires for a feature are
covered: e2e tests, docs, telemetry, no error-link path applies.
cc @sokra
Closes#93044
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
The Cloudinary URL for the overlay screenshot
(`assets.vercel.com/image/upload/v1645118290/docs-assets/static/docs/error-handling/overlay.png`)
404s. The line above it had a `{/* TODO UPDATE SCREENSHOT */}` comment,
so this was already flagged as known-stale.
Removes the broken image and its `"Here is an example of an overlay:"`
intro line. The paragraph above still explains what the overlay is, so
the page reads cleanly without the screenshot. If someone produces a
fresh screenshot later, restoring the markdown with an updated URL is a
one-line change.
### Affects
-
`nextjs.org/docs/pages/building-your-application/configuring/error-handling`
(stable, on next stable cut)
-
`nextjs.org/docs/canary/pages/building-your-application/configuring/error-handling`
(canary)
### Older versions
The same broken URL is present in v13/v14/v15 of the docs (synced from
the corresponding release tags). Backport is a separate call for
maintainers.
## What?
Adds documentation and TypeScript types for `import.meta.glob` in
Turbopack, and implements additional edge cases for Vite compatibility.
### Documentation
- New `## import.meta.glob` section in the [Turbopack reference
page](/docs/app/api-reference/turbopack) covering: lazy/eager loading,
named imports, query strings (including the object form), multiple
patterns, negation, TypeScript types, and a full options table.
- `import.meta.glob` row added to the Module Resolution feature table.
- Migration note added to the [from-Vite
guide](/docs/app/guides/migrating/from-vite) explaining that
`import.meta.glob` works out of the box with Turbopack, with
before/after for the deprecated `as` → `query` migration.
### TypeScript types
- Added `ImportMetaGlobOptions` interface and overloaded `glob()`
signatures to `ImportMeta` in `packages/next/types/global.d.ts`.
- The overloads return `Record<string, unknown>` when `eager: true` is
passed and `Record<string, () => Promise<unknown>>` otherwise.
### Implementation (edge cases)
- **`import: '*'`** (namespace import): Treated the same as omitting the
`import` option (returns the whole module namespace), matching Vite
semantics. Previously would have generated broken `module["*"]` access.
- **`query` as object literal**: `{ query: { bar: 'foo', raw: true } }`
is now supported. Keys and values are URL-encoded and joined into a
query string (`?bar=foo&raw=true`).
- **Stricter option validation**: A non-object-literal second argument
(e.g. `import.meta.glob('./*.js', 'eager')`) is now a compile-time error
instead of a warning, since the options cannot be safely defaulted.
### Tests
- Execution tests for new features: namespace `import: '*'`, query
object, combining query with negation, explicit dotfile patterns.
- New `import-meta-glob-errors` execution test with issue snapshots
covering: too many arguments, non-string pattern, and non-object options
argument.
## Why?
1. The feature shipped in #92640 but had no user-facing documentation
and no TypeScript types.
2. Several Vite-compatible edge cases were not handled: namespace
imports (`import: '*'`) and query objects. These gaps would cause subtle
behavioral differences for users migrating from Vite.
3. Option validation was too lenient — invalid second arguments fell
through to defaults silently.
## How?
### Docs & types
- `docs/01-app/03-api-reference/08-turbopack.mdx` — new section, table
row, TypeScript subsection
- `docs/01-app/02-guides/migrating/from-vite.mdx` — migration note
- `packages/next/types/global.d.ts` — `ImportMetaGlobOptions` +
`ImportMeta.glob()` overloads
### Implementation
-
`turbopack/crates/turbopack-ecmascript/src/references/import_meta_glob.rs`:
- `parse_import_meta_glob()`: `import: '*'` normalized to `None`; query
object parsing via `urlencoding::encode()`; non-object options argument
now emits an error and returns `None`.
- `turbopack/crates/turbopack-ecmascript/Cargo.toml`: added
`urlencoding` workspace dep.
Note: dotfile/dot-directory filtering is not needed in
`import.meta.glob` itself — the underlying `Glob` pattern engine and
`read_glob_internal` already handle dotfile semantics correctly
(explicit `./.foo/*.js` matches, wildcards include them).
### Tests
-
`turbopack-tests/tests/execution/turbopack/resolving/import-meta-glob/`
— new cases for `import: '*'`, query object, query + negation, explicit
dotfile pattern, wildcard including dotfiles
-
`turbopack-tests/tests/execution/turbopack/resolving/import-meta-glob-errors/`
— new test suite for fatal parse errors with issue snapshots
### Checklist
- [x] `pnpm prettier-fix` / `cargo fmt` run
- [x] Documentation follows the [docs contribution
guide](https://nextjs.org/docs/community/contribution-guide)
- [x] Execution tests pass (`cargo test -p turbopack-tests --test
execution -- "import_meta_glob"`)
- [x] `cargo clippy` clean
- [x] `pnpm --filter=next types` passes
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
### What?
Adds a new documentation guide at
`docs/01-app/02-guides/displaying-dates.mdx` that documents how to
display locale-aware dates and times on prerendered/static pages without
a visible flash when the page loads.
### Why?
Dates and times formatted with `toLocaleDateString()` or
`Intl.DateTimeFormat` produce different output on the server (build-time
locale/timezone) vs the browser (user's locale/timezone). On prerendered
pages this causes a visible text change ("snap") when React hydrates and
re-renders with the correct locale.
There was no existing documentation for this common pattern. The inline
script approach (running a small `<script>` synchronously before browser
paint to correct the DOM) is the recommended technique for static/cached
pages.
### How?
**New guide** (`displaying-dates.mdx`) follows the progressive example
style from `public-static-pages.mdx`:
1. **Step 1**: Shows the problem — a static page with a server-formatted
date
2. **Step 2**: Fixes it with an inline `<script>` +
`suppressHydrationWarning`
3. **Step 3**: Extracts a reusable `<LocalDate>` component with `<time>`
element for accessibility/SEO
Also includes a comparison table for when to use alternative approaches
(`connection()`, Client Components with `useEffect`, fully dynamic
pages).
**Cross-references added:**
- `public-static-pages.mdx` — callout in the "Static components" section
where "the current time" is mentioned
- `connection.mdx` — callout noting the inline script alternative for
prerendered pages
### Improving Documentation
- Ran `prettier` to fix formatting
- Follows doc conventions (frontmatter, `switcher` pattern, `filename`
attributes, callout format)
<!-- NEXT_JS_LLM_PR -->
<!-- CURSOR_AGENT_PR_BODY_END -->
[Slack
Thread](https://vercel.slack.com/archives/C03S8ED1DKM/p1776095579605699?thread_ts=1776095579.605699&cid=C03S8ED1DKM)
<div><a
href="https://cursor.com/agents/bc-e7710133-5c01-4a8a-9147-1ccbc9b6dd89"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a
href="https://cursor.com/background-agent?bcId=bc-e7710133-5c01-4a8a-9147-1ccbc9b6dd89"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Joseph Chamochumbi <joseph.chamochumbi@vercel.com>
## Summary
- Add a "UI state preservation" section to the Cache Components
migration guide explaining that pages are now preserved via React's
`<Activity>` instead of unmounting on navigation
- Add `related` links pointing to the preserving UI state guide and
`cacheComponents` config reference
- Cross-link the migration guide from the preserving UI state guide for
bidirectional discovery
The migration guide previously only covered route segment config
replacements (`dynamic`, `revalidate`, `fetchCache`, `runtime`) but
didn't mention the fundamental navigation behavior change. Users
migrating to Cache Components need to know that cleanup logic, analytics
events, and state resets may behave differently.
---------
Co-authored-by: Joseph Chamochumbi <joseph.chamochumbi@vercel.com>
"auto" mode is the default. It does not need to be explicitly exported
"force-" modes suggest overriding framework heuristics "force-disabled"
disables prefetching for this segment "force-static" forces any
prefetching of this segment to be static "force-runtime" forces any
prefetching of this segment to do runtime prefetching
It's worth noting that when runtime prefetching we fetch the necessary
segment and all child segments in a single request. This means that a
deeper segment might specify disabled or static and still get
conditionally rendered as a runtime prefetch. This was already the
behavior of runtime prefetching and not changing in this PR just
something to call out since it may be confusing to folks trying to
understand how the implementation of prefetching actually work
### What?
Add a "Migrating from experimental flags" section to the
`cacheComponents` config reference page, documenting that
`experimental.useCache` and `experimental.cacheComponents` are
deprecated in favor of top-level `cacheComponents: true`.
### Why?
Three config paths exist with overlapping semantics
(`experimental.useCache`, `experimental.cacheComponents`,
`cacheComponents`), but no documentation explains their relationship or
how to migrate. The build output shows different labels for each
("Experiments: ✓ useCache" vs "Cache Components enabled"), making them
look like different features. Both human developers and AI agents
encounter this ambiguity and may use the wrong flag.
### How?
Added a before/after code example to
`docs/01-app/03-api-reference/05-config/01-next-config-js/cacheComponents.mdx`
showing the migration from either experimental flag to the top-level
config, with a note that both experimental flags are deprecated.
---------
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
### What?
Adds a new `experimental.turbopackLocalPostcssConfig` option and
corresponding `PostCssConfigLocation::LocalPathOrProjectPath` variant so
Turbopack can resolve `postcss.config.js` starting from the CSS file's
directory first, falling back to the project root.
Also fixes an issue that surfaces when multiple per-directory
`postcss.config.js` files are used:
**Evaluate pool asset name collisions** — output file names for the
Node.js evaluate pool are now derived from an xxh3 hash of the full
module ident rather than the bare file name, so multiple
`postcss.config.js` files in different directories no longer collide.
### Why?
Currently Turbopack uses `ProjectPathOrLocalPath` which checks the
project root first, then falls back to the local directory. This means
per-directory `postcss.config.js` files cannot override the root config
— the root config always wins. For projects that need different PostCSS
transforms in different directories (e.g. a monorepo with multiple apps
or style directories), the more specific config should take precedence.
### How?
**Turbopack PostCSS resolution**
(`turbopack/crates/turbopack-node/src/transforms/postcss.rs`):
- Added `PostCssConfigLocation::LocalPathOrProjectPath` enum variant
with doc comments on all variants
- Refactored `find_config_in_location()` to express the search order as
a `Vec<FileSystemPath>` built from a single `match`, making each
variant's strategy immediately clear
**Evaluate pool asset file naming**
(`turbopack/crates/turbopack-node/src/evaluate.rs`):
- Replaced file-name-based output path derivation with a deterministic
xxh3 hash of the full module ident
- Adds `turbo-tasks-hash` dependency to `turbopack-node`
- Eliminates name collisions when multiple entries share the same file
name (e.g. per-directory `postcss.config.js` files)
**Next.js config flag** (`packages/next/src/server/config-shared.ts`,
`config-schema.ts`):
- Added `experimental.turbopackLocalPostcssConfig: boolean` option
(opt-in, default `false`)
- Wired through `crates/next-core/src/next_config.rs` → both client and
server contexts (`next_client/context.rs`, `next_server/context.rs`)
- When `true`, uses `LocalPathOrProjectPath`; when `false`/unset, uses
the existing `ProjectPathOrLocalPath` (no behavior change by default)
**E2E test** (`test/e2e/app-dir/turbopack-postcss-multiple-configs/`):
- Runs in both **Turbopack dev and production modes** — skips webpack
only (webpack does not support function-valued PostCSS plugins and the
`turbopackLocalPostcssConfig` flag is Turbopack-specific)
- 5 style directories, each with its own `postcss.config.js` passing a
**unique color option** (`blue`, `purple`, `orange`, `cyan`, `magenta`)
to a shared PostCSS plugin, proving that per-directory config resolution
produces distinct results rather than all sharing the same transform
- Root `postcss.config.js` is a no-op — if only the root config were
used, CSS would remain unmodified
- Test validates all 15 elements (5 dirs × 3 files) render with CSS
module classes
- Test collects all CSS (inline `<style>` tags for dev, linked `.css`
files for production) and asserts each directory's expected color is
present and `color: red` is absent
**Documentation**
(`docs/01-app/03-api-reference/05-config/01-next-config-js/turbopackLocalPostcssConfig.mdx`,
`docs/01-app/03-api-reference/08-turbopack.mdx`):
- New dedicated reference page for
`experimental.turbopackLocalPostcssConfig` covering usage, behavior
table, and a directory-tree example
- Added to the experimental options table in the Turbopack API reference
## Test plan
- [x] `cargo check -p turbo-tasks-backend` passes
- [x] `cargo check -p turbopack-node` and `cargo check -p next-core`
pass
- [x] E2E test `turbopack-postcss-multiple-configs` validates
per-directory PostCSS config resolution across 5 directories with
distinct per-directory colors (Turbopack dev and production modes)
- [x] Test correctly skips in webpack mode and deployment
- [x] Documentation page and experimental options table updated
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
## Summary
- add the missing `transitionTypes` entry to the `next/link` version
history table
- document that the prop was added in `v16.2.0`
## Testing
- not run (docs-only change)
<!-- NEXT_JS_LLM_PR -->
Closes: https://github.com/vercel/next.js/issues/86379
A native db driver, or sync db driver, won't be detected as external
data, rather, as sync I/O.
Main motivation is that better-sqlit3 is popular, and Node.js has
introduced a native sql driver module.
Given no other uncached data, or runtime data access, these queries
would be included in the static shell. Wrapping with Suspense is not
sufficient.
### What?
Add [`updateTag`](/docs/app/api-reference/functions/updateTag) to the
`cacheTag` API page: a **Related** link and a **Good to know** bullet
that explains when to use it (read-your-own-writes / immediate expiry)
and points to [Differences from
`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/updateTag#differences-from-revalidatetag).
### Why?
The bundled `cacheTag` doc only walked through `revalidateTag`, so the
two-API invalidation model was easy to miss. This keeps existing
`revalidateTag` wording and examples unchanged and adds a minimal
pointer to `updateTag` without duplicating its full reference.
### How?
- `related.links`: include `app/api-reference/functions/updateTag`.
- `## Good to know`: one imperative bullet + deep link to the comparison
section on the `updateTag` page.
## PR checklist (Improving Documentation)
- Prettier/ESLint ran via lint-staged on commit; run `pnpm prettier-fix`
if needed.
- Follow the docs contribution guide:
https://nextjs.org/docs/community/contribution-guide
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
## Summary
- Updated the "Built-in error recovery" bullet in `catchError` docs to
say "retries rendering the error boundary's children with fresh data in
Server Components" instead of "re-fetches and re-renders the error
boundary's children, including Server Components"
### What?
Update the "Good to know" callout in the `cacheLife` docs to explicitly
state that `cacheLife()` must be called inside a function body, not at
module scope, and that doing so throws an error.
### Why?
Calling `cacheLife('days')` at module scope in a file with `'use cache'`
at the top throws: `` `cacheLife()` can only be called inside a "use
cache" function ``. This is technically correct (module scope is not
inside a function), but the previous docs only said `cacheLife` "should
be placed within the function" without mentioning it errors. Both
developers and AI agents hit this when they place `cacheLife()` next to
the file-level `'use cache'` directive at the top of the module.
### How?
Replaced the soft "should be placed within the function" language with
explicit "must be called inside a function body, not at module scope"
and noted that it throws an error otherwise.
## PR checklist (Improving Documentation)
- [x] Run `pnpm prettier-fix` to fix formatting issues before opening
the PR
- [x] Follow the docs contribution guide:
https://nextjs.org/docs/community/contribution-guide
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
### What?
Add a `middleware.mdx` deprecation stub to the app-router file
conventions docs so that searching for `*middleware*` in the bundled
docs returns a result.
- `docs/01-app/03-api-reference/03-file-conventions/middleware.mdx` —
explains the v16 rename and links to the `proxy.js` API reference,
including the codemod command.
### Why?
The `middleware.ts` convention was renamed to `proxy.ts` in v16, but
"middleware" remains the widely understood term. Searching
`node_modules/next/dist/docs/01-app` for any file named `*middleware*`
currently returns zero results, which is confusing for both human
developers and AI coding agents that look up documentation by concept
name. This stub ensures the term "middleware" is discoverable and
immediately points users to the correct `proxy` docs.
### How?
One new `.mdx` file — no changes to existing files. The `copy_docs`
build task already globs `**/*` and renames `.mdx` → `.md`, so it will
automatically appear as `middleware.md` in `dist/docs/` on the next
build.
## PR checklist (Fixing a bug)
- This is a docs-only change, no code or tests affected.
- See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors should have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
### What?
Add a **Good to know** callout on the
[`cacheComponents`](https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents)
API reference, placed after the Usage list and before **Navigation with
Activity**.
### Why?
Developers enabling `cacheComponents: true` may see routes built as
partial prerenders (`◐ (Partial Prerender)`) without ever turning on a
separate PPR flag. The Caching getting-started guide already explains
PPR as the default with Cache Components; this surfaces the same idea on
the config page people often open first, and points to migration context
for Next.js 15 experimental PPR.
### How?
Single callout: PPR is used with the flag, legacy `experimental.ppr` /
`experimental_ppr` are superseded, `◐` in `next build` is expected, with
links to [How rendering
works](https://nextjs.org/docs/app/getting-started/caching#how-rendering-works)
and [Partial Prerendering
(PPR)](https://nextjs.org/docs/app/guides/upgrading/version-16#partial-prerendering-ppr)
in the Version 16 upgrade guide.
## PR checklist (Improving Documentation)
- Ran Prettier via lint-staged on commit; run `pnpm prettier-fix`
locally if CI flags anything.
- Follow the docs contribution guide:
https://nextjs.org/docs/community/contribution-guide
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
## Summary
Addresses a DX friction log finding and two minor clarifications in the
view transitions guide.
### Changes
- Add note explaining that `import { ViewTransition } from 'react'`
works because Next.js rewrites it to its internal React canary build via
bundler aliases (friction log finding)
- Clarify that shared element morph works without any CSS; `share` is
only needed to customize the animation
- Minor formatting fix in Next steps links
## Test plan
- [ ] Verify guide renders correctly in docs preview
---------
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Some corrections to the adapters docs.
- The example deploy script ran `cat .next/BUILD_ID` before `pnpm
build`. Under `set -euo pipefail` that kills the script immediately
since the file doesn't exist yet. Moved the build step first.
- `resolveRoutes` requires `URL` and `Headers` objects, not strings. The
example would cause TypeScript/runtime errors.
- Also documented the 6 return fields ~ other adapters might need to use
all of them for redirects, external rewrites, and middleware
short-circuiting.
### What?
Adds a `NEXT_HASH_SALT` environment variable **and** a
`experimental.outputHashSalt` config option that mix a user-supplied
string into every content-addressed hash used to generate chunk
filenames and static asset filenames. This works for both Webpack and
Turbopack.
When both are set, the values are concatenated (`outputHashSalt +
NEXT_HASH_SALT`), so a per-project salt can be baked into
`next.config.js` while a per-deployment salt is injected at build time
via the environment variable.
### Why?
Content-addressed filenames (e.g. `chunk.abc123.js`) are derived from
file content, so they only change when the content changes. There are
deployment scenarios where you need to force all filenames to rotate —
for example after a CDN misconfiguration has poisoned caches for a
particular hash space — without actually changing source code. A stable,
opt-in salt lets operators do this without touching application code.
Some customers prefer the config-file approach
(`turbopack.outputHashSalt`) over environment variables, so both are
supported.
### How?
**Webpack** already has `output.hashSalt` in its config. We simply
forward `NEXT_HASH_SALT` to that option.
**Turbopack** required threading the value through several layers:
1. The effective hash salt is computed once in
`assignDefaultsAndValidate` as `config.turbopackHashSalt =
(turbopack.outputHashSalt ?? '') + (NEXT_HASH_SALT ?? '')` and stored on
`NextConfigComplete`. Both `turbopackBuild` (production) and
`createHotReloaderTurbopack` (dev) read from this single field.
2. `ProjectOptions.hash_salt` receives the pre-computed salt.
3. `Project` stores the salt and passes it into the three chunking
context option structs (`ClientChunkingContextOptions`,
`ServerChunkingContextOptions`, `EdgeChunkingContextOptions`).
4. Both `BrowserChunkingContext` and `NodeJsChunkingContext` gain a
`hash_salt: RcStr` field.
5. A new `deterministic_hash_with_salt(salt, input, algorithm)` function
in `turbo-tasks-hash` writes the salt bytes first, then the content
bytes, into a single hasher — one pass, no hash-of-hash composition.
6. A matching `content_hash_with_salt` method is added to `FileContent`
and `AssetContent`.
7. `ChunkingContext::asset_path` is changed to accept `Vc<AssetContent>`
(instead of a pre-computed `Vc<RcStr>`) so the chunking context can
choose the correct hash path itself. `StaticOutputAsset::path`
simplifies accordingly.
Without `NEXT_HASH_SALT` and without `turbopack.outputHashSalt` set,
behaviour is identical to before — no hash change, no performance
impact.
**e2e test** (`test/production/app-dir/hash-salt/`) verifies:
- Two builds with the same salt produce identical chunk and static asset
filenames.
- A build with a different salt produces different filenames.
- `turbopack.outputHashSalt` (config) changes filenames vs no salt.
- Combined config + env salt differs from either alone.
- Runs for both Turbopack and Webpack.
---------
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Luke Sandberg <lukesandberg@users.noreply.github.com>
## What
Adds documentation for the `id`, `filePath`, and `pathname` properties
in the `STATIC_FILE` adapter output type.
## Why
These three string properties were previously undocumented or had
ambiguous descriptions. Based on feedback from the team:
- `id` is a unique identifier for the static file output
- `filePath` is the absolute filesystem path to the built file
- `pathname` is the routable URL pathname
## Changes
- Added JSDoc comments to the `STATIC_FILE` type definition in
`build-complete.ts`
- Updated the MDX documentation in `output-types.mdx` with clearer
inline comments
### What?
Add a before/after migration snippet to the version 16 upgrade guide
showing the old single-argument `revalidateTag('posts')` and the new
required two-argument `revalidateTag('posts', 'max')` signature. Also
mention the TypeScript error users will see and the `updateTag`
alternative for immediate expiration.
### Why?
Users upgrading from Next.js 15 to 16 encounter `Expected 2 arguments,
but got 1` when using the old single-argument syntax. The upgrade guide
previously only showed the new code without showing the old pattern,
requiring users to search externally to understand what changed.
### How?
- Added a concise `// Before` / `// After` code block showing the
migration, matching the convention used elsewhere in the upgrade guide
(e.g. `next/legacy/image`)
- Added a note that the single-argument form produces a TypeScript error
- Added a note pointing to `updateTag` as the alternative for immediate
expiration
- Kept the original full example with stale-while-revalidate behavior
explanation
## PR checklist (Improving Documentation)
- Run `pnpm prettier-fix` to fix formatting issues before opening the PR
- Follow the docs contribution guide:
https://nextjs.org/docs/community/contribution-guide
## Summary
- add a Good to know note in the CDN Caching guide
- clarify that if Proxy/Middleware runs behind the CDN cache,
middleware-dependent requests should bypass cache so Proxy/Middleware
remains the source of truth
---------
Co-authored-by: Joseph <sephxd1234@gmail.com>
## Summary
- move adapter implementation docs from
`app/api-reference/config/next-config-js/adapters` to
`app/api-reference/adapters`
- keep `adapterPath` as an entry page that points to the new adapters
section
- add matching Pages Router pointer docs under
`pages/api-reference/adapters`
- update existing docs links that referenced old `adapterPath#...`
anchors to the new adapters routes
### What?
Update the "Good to know" note in the `use cache` directive docs to
clarify that functions and components using `use cache` must be `async`
at all levels — not just at file level.
### Why?
Users get the build error `"use cache" functions must be async
functions` when applying the directive to a synchronous component. The
existing note only mentions the file-level case ("When used at file
level, all function exports must be async functions"), which implies the
async requirement doesn't apply at component or function level.
### How?
Updated the "Good to know" callout to state the requirement applies to
all functions and components using `use cache`, with the file-level case
as additional context.
## PR checklist (Improving Documentation)
- Run `pnpm prettier-fix` to fix formatting issues before opening the PR
- Follow the docs contribution guide:
https://nextjs.org/docs/community/contribution-guide
---------
Co-authored-by: Joseph Chamochumbi <joseph.chamochumbi@vercel.com>
### What?
Updated all documentation examples of `revalidateTag` to use the new
two-argument signature (`revalidateTag(tag, 'max')`) instead of the
deprecated single-argument form (`revalidateTag(tag)`).
### Why?
The `revalidateTag` API now requires a second `profile` argument. Using
the old single-argument form causes a TypeScript build error: "Expected
2 arguments, but got 1". Several documentation pages still showed the
old signature, which would lead developers to write code that fails to
compile.
### How?
Updated 14 call sites across 6 documentation files to pass `'max'` as
the second argument, which is the recommended default for
stale-while-revalidate semantics:
- `docs/01-app/02-guides/incremental-static-regeneration.mdx`
- `docs/01-app/02-guides/redirecting.mdx`
- `docs/01-app/02-guides/caching-without-cache-components.mdx`
- `docs/01-app/02-guides/backend-for-frontend.mdx`
- `docs/01-app/02-guides/how-revalidation-works.mdx`
- `docs/01-app/03-api-reference/04-functions/cacheTag.mdx`
The `revalidateTag.mdx` API reference page and `09-revalidating.mdx`
getting started page were already updated. The `self-hosting.mdx` cache
handler method signature (`async revalidateTag(tags)`) is unrelated and
left unchanged.
### What?
Change the `opengraph-image` return type to only `Response`.
### Why?
This function must return a `Response` object. The other documented
types cause Next.js builds to fail. I found this out the hard way by
following the documentation.
### How?
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
Updates the AGENTS.md snippet in the AI agents guide to match the wording shipped in the bundled docs. The previous version used softer language ("ALWAYS read docs before coding") that diverged from what `node_modules/next/dist/docs/` actually ships. This aligns the two so agents see consistent instructions regardless of where they read them.
## Description
Documents the CSS decimal precision difference between Turbopack and
webpack in the Turbopack docs page under "Known gaps with webpack".
Turbopack uses Lightning CSS which outputs 5 digits of decimal precision
for numeric CSS values, while webpack uses 10 digits. This applies to
both plain CSS and Sass/SCSS output.
Closes https://github.com/vercel/next.js/issues/91862
## Summary
- Introduces the **verified adapter** concept to deployment docs: open
source adapters that run the Next.js compatibility test suite, hosted
under the Next.js GitHub org
- Splits the adapter list on the deploying page into **Verified
Adapters** (Vercel, Bun) and **Other Platforms** (unverified
integrations)
- Documents the Next.js team's commitments for verified adapters and
Ecosystem Working Group participants (coordinated testing, early access,
direct support)
- Notes that Cloudflare and Netlify are working on verified adapters
built on the Adapter API
## Test plan
- [ ] Verify links resolve correctly (adapter API reference, test suite
anchor, ecosystem working group page)
- [ ] Confirm markdown table renders properly with the longer "Varies"
cell
- [ ] Review wording with stakeholders (Cloudflare, Netlify, Bun)
---------
Co-authored-by: Tim Neutkens <tim@timneutkens.nl>