mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
codex/fallback-root-cache
3801 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7b58e5880c |
Turbopack: Add support for specifying additional roots (#98003)
Full motivation and plan here: https://app.notion.com/p/vercel/Turbopack-pnpm-Global-Virtual-Store-383e06b059c480579403ddfd71cc2d40?source=copy_link The goal is to allow `DiskFileSystem` to traverse outside of it's own root to other configured `DiskFileSystem`s when following symlinks. We may allow traversal in other situations in the future, but this is limited to symlink resolution for now. ## Global Virtual Store The motivation for this is to enable [pnpm's Global Virtual Store feature](https://pnpm.io/global-virtual-store) (and there are other package managers doing this, including nub and bun). We'd expose the ability to manually configure this in `next.config.js`, but we should also auto-configure ourselves for popular package managers (or at least make a best effort to do so, the `PNPM_HOME` semantics can be complicated). The `ignoreIfMissing` option is provided for this situation: We can configure a bunch of roots automatically, and they only actually get set up if they exist, the check for directory existence is cheap. ## NFT changes This requires a couple extensions to the `*.nft.json` file format: https://github.com/vercel/next.js/pull/98469 ## Related Issues - #93556 - https://github.com/pnpm/pnpm/issues/14972 |
||
|
|
41ef17c645 |
Add experimental agent feedback workflow (#98582)
## Summary Adds an off-by-default `experimental.agentFeedback` workflow for collecting Next.js friction without interrupting the user’s task. - `next dev` writes a small managed block to the project agent-instructions file. - Agent entry points, including `next-dev-loop`, queue possible issues instead of opening duplicate forms. - At the final stopping point, an internal command checks the remote gate and returns the reporting protocol bundled with that Next.js version. The agent attempts to anonymize each qualifying issue and opens a separate review form once. If the browser does not open, it prints the URL for the user without troubleshooting the failure. - Report links no longer contain a public page token. Nothing is sent until the user submits the form, which remains rate-limited and uses a server-only ingest credential. - Disabling `agentFeedback` or `agentRules` removes only its managed block on the next `next dev`. Empty generated agent files are cleaned up; user-authored content is preserved. - Adds API references for both options and updates the AI agents guide. Bundling the protocol keeps the managed block small and allows the report format to evolve with each Next.js version. The receiving form is implemented in [vercel/front#85739](https://github.com/vercel/front/pull/85739) and should deploy before this workflow is enabled. ## Verification - `NEXT_SKIP_ISOLATE=1 pnpm test-dev-turbo test/development/app-dir/agent-rules-auto-generate/agent-rules-auto-generate.test.ts` - `pnpm jest packages/next/src/server/lib/generate-agent-files.test.ts packages/next/src/cli/internal` - `npx eslint --config eslint.config.mjs packages/create-next-app/helpers/generate-agent-files.ts packages/next/src/server/config-shared.ts packages/next/src/server/lib/generate-agent-files.ts test/development/app-dir/agent-rules-auto-generate/agent-rules-auto-generate.test.ts` <!-- NEXT_JS_LLM --> |
||
|
|
058456fac6 |
Set experimental.agenticAutoUpgrade after successful upgrade (#98871)
So the framework can nudge agent/human in the future |
||
|
|
d3d30cf88a |
Add future defaults upgrade coverage for next upgrade --ai (#98643)
Stacked on #98640. This PR adds `next upgrade --ai="future"` flag, which is targeted to help users leverage agents to upgrade their app to adopt the future defaults when available. Just like latest version upgrade, it covers running codemods and a migration checklist for major-to-major upgrades to support breaking changes more reliably. This PR currently covers Cache Components only for the future default. |
||
|
|
97496b2078 |
Add "latest" upgrade coverage for next upgrade --ai (#98633)
Stacked on #98637. This PR adds `next upgrade --ai="latest"` flag, which is targeted to help users leverage agents to upgrade their app to the latest major version when available. Just like security upgrade, it covers running codemods and a migration checklist for major-to-major upgrades to support breaking changes more reliably. |
||
|
|
9bb13254ae |
Nudge the agents for security vulnerable version upgrade (#98637)
Stacked on #98562. > [!TIP] > Recommended to review commit by commit. This PR adds `experimental.agenticAutoUpgrade = 'security'` config which enables nudging the agents to notify the user when the app's Next.js version has any security advisories. The nudge will include guiding to upgrade via `next upgrade --ai` (run "security" by detecting config). The method of nudging leverages the agents behavior where they tend to listen to messages from fatal errors that blocks the process compared to general error/warning logs. Whenever the agents run `next dev` or `next build`, Next.js will detect the condition and nudge the agent using this method. Afterwards it's up to the user whether to proceed the upgrade or not, it's 100% up to the user how to run it e.g. subagent, background agent, etc. and the process should not enforce any that affects user's workflow. |
||
|
|
5d9ab72cef |
Add next upgrade --ai and security vulnerability coverage (#98562)
> [!TIP] > Recommended to review commit by commit. This PR adds `next upgrade --experimental-ai="security"` flag (alias `--ai`), which is targeted to help users leverage agents to upgrade their app to the safe major version when their app's Next.js version has any security advisories. Once the command is ran from the user, Next.js will detect the installed agent harness in user's device, currently limited to Codex and Claude, and will proceed with starting an agent session once approved. If it is called within an agent session, the work will continue off within that agent. `next upgrade --ai` simply does two things: - prepare the relevant context to temporary dir - print hand off prompt, guiding to read those context The context will guide the agent to run relevant codemods and migration checklist to proceed. This PR is a base core of the workflow, and will have wrappers of entry point around this. Also, will add "latest" and "future" as follow up, which will cover the app to be always latest, and adopt the future defaults like Cache Components. This PR also sets up the evals infra and adds evals. |
||
|
|
fbe0d56219 |
docs: rewrite the bots and crawlers section in the caching guide (#98782)
## Summary The Bots and crawlers section in the caching guide said that all bots and crawlers skip the static shell because "they need a complete document". Only HTML-limited bots do, and the reason is metadata: with Partial Prerendering, `generateMetadata` can resolve dynamically and stream in after the shell, which puts the tags in the body rather than the `<head>`. Bots that need them in the `<head>` get a dynamic render instead. The second paragraph now states the consequence directly: if the build-time prerender read something unreachable at request time, the render throws and those bots get a 500. Which user agents this covers, and the `htmlLimitedBots` option for changing the list, stay owned by the Streaming guide, which the section links to. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
6130a49316 |
docs: scope React cache() guidance to the render pass (#98741)
Reimpl of: https://github.com/vercel/next.js/pull/98579 |
||
|
|
95d3d8ec81 |
docs: clarify metadata support in root not-found (#98670)
docs: document metadata/generateMetadata in root `app/not-found.js` as it works, but docs don't specify it |
||
|
|
e29a96a9ee |
docs: add an intro paragraph to the glossary (#98654)
The glossary page goes straight from frontmatter to the first entry, `# A` — the word "glossary" appears nowhere in its body. We index docs for search from the rendered content, and the page title is stored as non-searchable metadata, so searching "glossary" on nextjs.org does not return the glossary. This adds a two-sentence intro, which gives the page a body chunk carrying its own name. ```mdx The Next.js documentation uses a recurring set of terms for routing, rendering, and caching. This glossary defines them. ``` Also reads better than a page that opens directly to content. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Aurora Scharff <66901228+aurorascharff@users.noreply.github.com> |
||
|
|
25fe6f6f06 |
docs: document default turbopack.resolveExtensions (#98627)
Fixes: #98609 Editor note: Ensure this lands well into https://github.com/vercel/next.js/pull/98407 too |
||
|
|
996f957c06 |
Document codemod errors and intentional migration exceptions (#98565)
Stacked on #98560. ### Why? Users need to distinguish unfinished codemod repairs from intentional feature-adoption opt-outs. ### How? Document `@next-codemod-error` and temporary `UnsafeUnwrapped*` casts in the codemod guide, with examples and a link to the Async Request APIs migration guide. Explain when an inapplicable suggestion can be marked `@next-codemod-ignore` with a reason. Show the ignore comment emitted for Cache Components opt-outs and explain that it does not block compilation. |
||
|
|
30d6d8f497 |
Adds this.mode for webpack loaders (#98532)
Pretty straightforward. But a number of loaders depend on it including - postcss-loader - sass-loader - stylus-loader - vue-loader - nunjucks-loader - thread-loader |
||
|
|
393de1ebe0 |
skill: add Partial Prefetching optimizer (#96471)
## Summary - add a Partial Prefetching optimizer Skill for selected source Links and destinations - keep prefetch stages and Link policy in the public [Optimizing prefetching guide](https://nextjs.org/docs/app/guides/optimizing-prefetching), and rely on the same static-shell documentation as the Cache Components optimizer for cache placement, Suspense, loading UI, and authorization - keep the Skill focused on the product decision, production `instant()` loop, differential verification, and mutation freshness when an optimization adds a cache around mutable data - reference the canonical [`unstable_prefetch()`](https://nextjs.org/docs/app/api-reference/functions/prefetch) and [`unstable_navigation()`](https://nextjs.org/docs/app/api-reference/functions/navigation) docs instead of explaining the APIs again in the Skill - link cache-writing optimizations to the canonical [Revalidating](https://nextjs.org/docs/app/getting-started/caching-and-revalidating#revalidating) documentation and verify a populated-cache mutation in the Skill and eval - bundle a self-contained production rig aligned with the related adoption and optimizer Skills, tuned to record each source-link and prefetch-stage contract - add an eval that compares baseline, the bundled guide, and the local Skill against the same prefetched-UI and cache-freshness goal - document the optimizer in the AI agents guide This follows the same guide/Skill split as #97737. Partial Prefetching adoption preserves existing behavior; this optimizer is the later decision about what is worth preparing before a click and what should wait for navigation. Related: #97712, #97737, #97759, #98122, #98175 ## Eval One run per treatment with Claude Opus 4.8 and Claude Sonnet 4.5, using a fixed Claude Haiku 4.5 judge. The bundled-guide and Skill treatments use a local Next.js tarball containing the new guide and API references. | Model | Treatment | Criteria | Score change | Overall | Duration | Tokens processed | Total run cost | Cost change | | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | Opus 4.8 | Baseline | 10/10 (100%) | — | Pass | 18m 42s | 3.39M | $3.46 | — | | Opus 4.8 | Bundled guide | 9/10 (90%) | -10 pp | Fail | 13m 37s | 3.02M | $2.84 | -18.0% | | Opus 4.8 | Local Skill | 10/10 (100%) | +10 pp | Pass | 17m 22s | 2.80M | $2.54 | -10.6% | | Sonnet 4.5 | Baseline | 2/10 (20%) | — | Fail | 15m 55s | 1.97M | $1.06 | — | | Sonnet 4.5 | Bundled guide | 5/10 (50%) | +30 pp | Fail | 15m 49s | 2.17M | $1.11 | +4.5% | | Sonnet 4.5 | Local Skill | 8/10 (80%) | +30 pp | Fail | 15m 31s | 1.41M | $0.77 | -30.4% | Token totals count each unique model message once and include uncached input, cache creation, cache reads, and output. Cache reads account for most of the total in every treatment. Total run cost applies the current AI Gateway base rates to each token category: Opus 4.8 costs $5/M input, $6.25/M cache writes, $0.50/M cache reads, and $25/M output; Sonnet 4.5 costs $3/M input, $3.75/M cache writes, $0.30/M cache reads, and $15/M output. The six agent runs cost $11.78 in total. Compared with baseline, the Opus Skill run maintained a 100% criteria pass rate while reducing cost by 26.7%. The Sonnet Skill run improved the criteria pass rate from 20% to 80%, a 60 percentage-point increase, while reducing cost by 27.3%. The stronger model could solve the fixture without the Skill, although it independently searched the bundled Next.js documentation and took slightly longer than the Skill treatment. The Opus guide-only run implemented the correct behavior but missed the required RED-before-GREEN proof. The Sonnet treatments show a clearer progression. Baseline did not implement the required caching, navigation stage, `instant()` contract, or cache invalidation. The bundled guide added the cached summary and production test, but missed the navigation stage, deferred-state assertions, tag invalidation, and RED-before-GREEN proof. The Skill treatment completed the `instant()` RED-to-GREEN loop and final production behavior, but left `connection()` in the related-sessions read and used path revalidation instead of tagging the cached read. It therefore passed 8 of 10 criteria but failed the overall eval. All six measured runs completed through grading. Chromium and its system dependencies are provisioned before the agent starts, so missing browser libraries did not determine these results. ## Verification - `pnpm --filter=next build` - `pnpm prettier --check` for the changed guide, prompt, Skill, and eval fixture - `pnpm exec tsc -p evals/evals/agent-051-optimize-partial-prefetching/tsconfig.json --noEmit` - `node --check run-evals.js` - `git diff --check` - packed a local Next.js tarball and confirmed it contains the optimizer guide plus the `navigation()` and `prefetch()` references - verified the `navigation()` contract in the minimal reproduction and NextBeats on Next.js 16.4.0-canary.22 - merged the latest `canary`, preserving both generic fixture setup and Playwright provisioning <!-- NEXT_JS_LLM --> |
||
|
|
ed1aab5d38 |
fix(turbopack): resolve ../ and /-rooted import.meta.glob patterns (#96557)
### What?
`import.meta.glob()` now resolves `../`-relative and
`/`-project-root-absolute
patterns (and `base`), validates every pattern (positive and negative)
for
whether it escapes the filesystem root, and shares the underlying
"resolve leading `./`/`../` against a directory" primitive with
`next-core::relativize_glob` (used by `outputFileTracingIncludes`)
instead of
duplicating it.
### Why?
`import.meta.glob()` handed its patterns straight to Turbopack's `Glob`,
which
matches relative to the scanned directory and understands neither a
leading
`/` nor `..` segments. `import.meta.glob('../../content/*.js')` and
`import.meta.glob('/content/*.js')` therefore silently returned `{}`,
and a
`base` that walked above the filesystem root failed with an internal
error
and a panic report instead of a normal diagnostic. A negative
(exclusion)
pattern that escaped the root was silently dropped, which is the worst
outcome for an exclusion — it would silently include files the user
asked to
exclude.
### How?
- Patterns are split into the directory they're rooted in and the
remaining
glob, matched relative to a single common scan directory covering every
positive and negative pattern. `/` is resolved from the project root,
matching Vite's rule that patterns are relative to the importing file or
absolute from the project root.
- Map keys follow the pattern too: `base` now produces base-relative
keys
(`'./zeta.js'` instead of `'../../content/zeta.js'`, matching Vite), a
project-root-absolute pattern produces project-root-absolute keys, and
origin-relative keys are unchanged otherwise.
- Every pattern is validated up front; one that leaves the project (a
`base`,
a positive pattern, or a negative one) reports an error issue at the
call
site and yields an empty object, instead of crashing or being silently
dropped.
- The `./`/`../`-resolution loop is extracted into
`turbo_tasks_fs::glob::relativize_glob`, a crate both `next-core` and
`turbopack-ecmascript` already depend on. `next-core::relativize_glob`
is
now a thin wrapper over it (same name, signature and error message); its
5
unit tests are removed since the shared primitive's own tests cover the
same cases.
<!-- NEXT_JS_LLM -->
Co-authored-by: Niklas Mischkulnig
<4586894+mischnic@users.noreply.github.com>
---------
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
|
||
|
|
2aaa6e810a |
docs: preserve route behavior during Cache Components adoption (#98275)
## Summary - Explain that removing `dynamic = 'force-dynamic'` moves a route from full request-time rendering to Cache Components' Partial Prerendering model, then document the choices for its request-time work. - Preserve routes that previously used `dynamic = 'force-static'` or `dynamic = 'error'` instead of leaving them under `instant = false`, which can move rendering to request time and reduce prefetched UI. - Clarify that existing `fetch` and `unstable_cache` caching can remain in place during adoption. - Combine the incremental checkpoint and completion coverage into one eval that verifies both stages of the migration. - Make the adoption skill honor an explicit incremental strategy before continuing through a complete migration. - Remove the unrelated `.env*` ignore and restore the existing agent-054 eval registration. ## Eval results `agent-047-adopt-cache-components` runs the complete incremental migration with `openai/gpt-5.6-luna` through the Codex harness. It retains the original five checks for the first shippable checkpoint and the original seven checks for completing the migration. | Treatment | Result | Notes | | --- | --- | --- | | Baseline | 11/12 | Preserved the final behavior, but treated the work as a fresh migration instead of explicitly continuing from the recorded first checkpoint. | | Guide (`AGENTS.md`) | 8/12 | Built successfully, but rewrote the existing `unstable_cache`, changed the catalog's hourly cadence, moved its timestamp to request time, and skipped the recorded incremental checkpoint. | | Adoption skill | 12/12 | Preserved both cache lifetimes and static contracts, recorded a passing incremental checkpoint, removed the remaining opt-outs, and completed the migration. | ## Verification - `pnpm build` - Luna eval runs for the baseline, guide, and adoption-skill treatments - Prettier and ESLint checks for the changed guide, skill, and eval files - `git diff --check` <!-- NEXT_JS_LLM --> |
||
|
|
526c492a60 |
docs: document React browser API (#97621)
### What? - Documents React Canary's new `browser` API in the SPA guide - Adds examples for marking a Client Component as browser-only and wrapping it in Suspense ### Why? The App Router includes React Canary releases, and the `browser` API now provides a Suspense-based option for components that depend on browser APIs such as `window` or `document`. ### How? The guide calls `browser()` inside `use()` in a Client Component. It then wraps that component in Suspense so prerendering can show the closest fallback before the component renders in the browser. ### Testing - `prettier --check docs/01-app/02-guides/single-page-applications.mdx` - `alex docs/01-app/02-guides/single-page-applications.mdx --quiet` (only existing SPA dictionary warnings) |
||
|
|
9486750ff2 |
[evals] Cover focused Cache Components and Partial Prefetching cases (#97813)
## Summary
- update existing evals for synchronous I/O, instant shell validation,
and Cache Components configuration instead of adding duplicate fixtures
- add focused Partial Prefetching coverage for URL-data inclusion, the
prefetch stage, and the navigation stage
- retain focused Cache Components coverage for session isolation and
URL-dependent shells
- rebase onto current `canary` so the Partial Prefetching evals can use
the public `unstable_prefetch()` and `unstable_navigation()` API
references
## Coverage
The latest results use Claude Sonnet 4.6 for the coding agent and Claude
Haiku 4.5 for the agentic judge. The Partial Prefetching cases have been
rerun after the new API references landed, and the corrected Cache
Components criteria have also been rerun.
| Eval | Scenario | Baseline | Bundled docs |
| --- | --- | ---: | ---: |
| `agent-035-connection-dynamic` | Adopt Cache Components while keeping
synchronous request-time values fresh and stable navigation in the shell
| 5/5 | 5/5 |
| `agent-040-instant` | Follow default validation and keep a stable
product title ahead of live inventory | 4/4 | 4/4 |
| `agent-042-enable-ppr` | Translate legacy route configuration while
preserving hourly and request-specific behavior | 4/4 | 4/4 |
| `agent-048-prefetch-url-data` | Include cached URL-specific category
content in a selected per-link prefetch | 3/6 | 6/6 |
| `agent-049-prefetch-stage` | Exclude cached content from the App Shell
but include it in a selected prefetch | 1/5 | 5/5 |
| `agent-051-prefetch-until-navigation` | Prefetch a selected title
while deferring the rest until navigation | 0/5 | 5/5 |
| `agent-052-cache-components-session-isolation` | Reuse shared data
without placing member-specific request data in a public cache | 5/5 |
5/5 |
| `agent-053-cache-components-url-shell` | Keep URL data below a useful
shell and include every output-changing value in the cache key | 3/5 |
5/5 |
After the public API references landed, the prefetch-stage and
navigation-stage evals show the intended documentation signal. Baseline
Sonnet passed 1/5 criteria for `unstable_prefetch()` and 0/5 for
`unstable_navigation()`. With the canonical bundled-docs instruction,
Sonnet found the new references and passed all 5/5 criteria in both
cases. After clarifying that the URL-data navigation must not block and
linking the general navigation docs to the optimizing-prefetching guide,
the URL-data case improved from 3/6 without docs to 6/6 with bundled
docs.
## Result interpretation
- `agent-048` now passes 6/6 with bundled docs. The scoped documentation
link led the agent to the URL-data prefetch pattern instead of a
blocking render. Baseline Sonnet added `prefetch={true}` but relied on
the previous full-payload behavior and missed both the Partial
Prefetching configuration and cached URL data, resulting in 3/6.
- The low baseline scores for `agent-049` and `agent-051` are the
intended signal. Without the new references, the agents did not discover
the prefetch and navigation stages. With bundled docs, both cases pass
5/5.
- `agent-053` is also an intended documentation signal: the baseline hid
the useful shell behind a broad fallback, while bundled docs preserved
the shell and passed 5/5.
- `agent-035` now passes 5/5 in both conditions after removing an
unnecessary requirement for visible loading UI in one small metadata
region. `agent-052` also passes 5/5 in both conditions after removing
two failures for an omitted explicit `cacheLife()`, because the default
cache profile still applies, and clarifying for the judge that an
uncached component reading `cookies()` or `headers()` remains
request-specific without another opt-out marker.
## Overlap decisions
- `agent-035` now carries the synchronous-I/O adoption case, so the
duplicate `agent-051` fixture was removed.
- `agent-042` now carries the route-configuration migration case, so the
duplicate `agent-048` fixture was removed.
- `agent-034` remains focused on correct async `cookies()` and
`headers()` usage. `agent-052` tests the separate risk of leaking
request-specific values through a public cache.
- `agent-040` tests one meaningful shell under default validation.
`agent-041` still tests independent streaming across several dashboard
sections.
- `agent-048` intentionally brings cached URL-specific content into a
selected prefetch. `agent-053` tests the opposite shared-shell
requirement during Cache Components adoption.
## Verification
- Dry fixture generation passes for all eight cases in the table.
- `pnpm build-all`
- `agent-049-prefetch-stage`: baseline 1/5, bundled docs 5/5
- `agent-051-prefetch-until-navigation`: baseline 0/5, bundled docs 5/5
- `agent-035-connection-dynamic`: baseline 5/5, bundled docs 5/5
- `agent-048-prefetch-url-data`: baseline 3/6, bundled docs 6/6
- `agent-052-cache-components-session-isolation`: baseline 5/5, bundled
docs 5/5
- `pnpm exec tsc -p
evals/evals/agent-035-connection-dynamic/tsconfig.json --noEmit`
- `pnpm exec tsc -p evals/evals/agent-042-enable-ppr/tsconfig.json
--noEmit`
<!-- NEXT_JS_LLM -->
|
||
|
|
b879776299 |
Adds this.target for webpack loaders (#98436)
There are a bunch of different loaders I found depending on this - vue-loader - svelte-loader - svg-sprite-loader - nunjucks-loader - workerize-loader - worklet-loader - nativescript-worker-loader - thread-loader - svg-sprite-generation-loader This doesn't by itself fix any of them fully, but does increase compatibility |
||
|
|
f8657de16c |
docs: clarify ISR with Suspense params (#98210)
## Summary - warn that awaiting params inside Suspense without Partial Prefetching prevents unlisted paths from using ISR - clarify that route data may still be cached even though the URL-specific route renders on each request - link the migration warning to the ISR with Cache Components guide ## Why This follows up on #98209. The migration eval showed that an agent can preserve generateStaticParams but move params beneath Suspense while Partial Prefetching is disabled. That produces a fallback shell, but unlisted paths do not use ISR. Context: https://github.com/vercel/next.js/discussions/98177#discussioncomment-18252316 ## Testing - git diff --check - Prettier check for both changed guides - commit hooks: Prettier and ESLint |
||
|
|
26b2bbe024 |
example: Use a Redis cacheHandler w/o dependencies (#95346)
Verify locally:
- Copy the example to a local directory
- Install deps
- docker compose up -d
- npm run build && npm run start
- navigate to localhost:3000/cet (or gmt)
- open the Redis view (link in localhost:3000/cet)
- revalidate data on /cet or /gmt
- verify it on the Redis view
I had to update the app to async params, updateTag, and write the
handler in such a way that it solved:
```
⨯ TypeError: p.segmentData.get is not a function
at ignore-listed frames
⨯ Error: failed to pipe response
```
Also adding docs edits with this caveat.
An agent review, and cross checking with a community implementation,
surfaced, that we had to read tags from
`data.headers['x-next-cache-tags']`. This agent review added a few
comments, I think they are useful, but can cut down if needed.
Last but not least, had to update the Time API endpoint (former no
longer worked).
|
||
|
|
552dd3538d |
docs: preserve Next.js development state (#98276)
## Summary Explain in the Building guide that `.next/dev` contains the active development server state and incremental compilation caches. Deleting or moving `.next` makes the development server recreate that state, while a separate `distDir` provides isolated production output. Make the `next-dev-loop` Skill explicit that moving `.next` to a backup is still a reset. Keep the AI coding agents guide focused on discovering and inspecting the running development server. Update the agentic regression eval so it leaves two working routes and their development server running before introducing an invalid `generateStaticParams` signature. The production error points into `.next/types`, but the source contains the defect. The eval independently checks that the agent fixes the source, preserves `.next`, and verifies the result through the existing development session. ## Eval results | Model | Treatment | Result | Source fix | Preserve `.next` | Existing dev verification | | --- | --- | --- | --- | --- | --- | | GPT-5.6 Luna | Baseline | 1/3 | Fail | Fail | Pass | | GPT-5.6 Luna | Bundled Guide via `AGENTS.md` | 2/3 | Pass | Fail | Pass | | GPT-5.6 Luna | `next-dev-loop` Skill | 3/3 | Pass | Pass | Pass | The baseline tried to clear `.next`, moved it aside when deletion was blocked, and did not leave a valid source fix. The Guide treatment read the relevant `generateStaticParams` API reference and fixed the source, but still moved `.next` because it did not retrieve the general Building guide. The Skill treatment read the explicit preservation guidance, kept the original `.next` directory and development server active, used a separate `distDir` for the production build, and verified both routes through the running app. ## Verification - `pnpm --filter=next build` - Agentic eval with GPT-5.6 Luna: baseline, bundled Guide, and `next-dev-loop` Skill - Prettier and ESLint checks on the changed files - `git diff --check` <!-- NEXT_JS_LLM --> |
||
|
|
afb0dbac6a |
Adds .resolve to loaders api (#98361)
This fully fixes handlebars loader in my tests and a number of other loaders depend on this as well. The implementation for getResolve is a bit different in turbopack, but this should be semantically the same. |
||
|
|
aab9387128 |
docs: document excluding content from a prefetch in the prefetching g… (#98175)
Guide introduction of unstable_prefetch and unstable_navigation. |
||
|
|
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. |
||
|
|
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 |
||
|
|
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 |
||
|
|
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.
|
||
|
|
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 --> |
||
|
|
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 --> |
||
|
|
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. |
||
|
|
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 --> |
||
|
|
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 < 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> |
||
|
|
b48cb74271 |
docs: document create-next-app Cache Components prompt and flag (#97798)
Follow up to: Add Cache Components option to create-next-app - #97695 |
||
|
|
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. |
||
|
|
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 --> |
||
|
|
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> |
||
|
|
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
|
||
|
|
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 # --> |
||
|
|
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. |
||
|
|
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. |
||
|
|
03a09695be |
docs: param access on use-server (#97865)
Correctness pass — following https://github.com/vercel/next.js/pull/97823 |
||
|
|
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> |
||
|
|
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 --> |
||
|
|
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. |
||
|
|
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)
|
||
|
|
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. |
||
|
|
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> |
||
|
|
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 |