With Cache Components enabled in development, fetches without an
explicit cache config were incorrectly routed through
`createCachedPrerenderResponse` during the cache filling phase due to
the `serverComponentsHmrCache` condition. This function awaits the
entire response before returning to the caller, which prevents
infinitely streaming responses from ever being read or aborted.
This is a regression that was introduced in #84088.
These fetches should be treated as dynamic and use
`createCachedDynamicResponse` instead, which returns immediately and
caches in the background with proper error handling. The fix adds
`isCacheableRevalidate` to the condition so only explicitly cacheable
fetches use the blocking path.
Additionally, the "Failed to set fetch cache" warning is now suppressed
when the request's abort signal was triggered, since aborting is
intentional.
Flakey:
```
❌ test/integration/app-dir-export/test/start.test.ts output:
HEADLESS=true NEXT_TELEMETRY_DISABLED=1 CI= NEXT_TEST_CI=true IS_RETRY=undefined TRACE_PLAYWRIGHT=true CIRCLECI= GITHUB_ACTIONS= CONTINUOUS_INTEGRATION= RUN_ID= BUILD_NUMBER= JEST_JUNIT_OUTPUT_NAME=test_integration_app-dir-export_test_start.test.ts JEST_SUITE_NAME=default:12/13:integration:test/integration/app-dir-export/test/start.test.ts /root/actions-runner/_work/next.js/next.js/node_modules/.bin/jest '--ci' '--runInBand' '--forceExit' '--verbose' '--json' '--outputFile=test/integration/app-dir-export/test/start.test.ts.results.json' 'test/integration/app-dir-export/test/start.test.ts'
[08:48:33.323Z] Running command "next build /root/actions-runner/_work/next.js/next.js/test/integration/app-dir-export"
▲ Next.js 16.1.0-canary.1
- Local: http://[::1]:36371
- Network: http://[::]:36371
✓ Starting...
Error: "next start" does not work with "output: export" configuration. Use "npx serve@latest out" instead.
at <unknown> (dist/server/next.js:230:53)
at async NextServer.prepare (dist/server/next.js:177:24)
at async initializeImpl (dist/server/lib/render-server.js:132:5)
at async initialize (dist/server/lib/router-server.js:538:22)
at async Server.<anonymous> (dist/server/lib/start-server.js:381:36)
[08:49:12.558Z] Running command "next build /root/actions-runner/_work/next.js/next.js/test/integration/app-dir-export"
FAIL webpack test/integration/app-dir-export/test/start.test.ts (100.119 s)
app dir - with output export (next start)
production mode
✓ should error during next start with output export (39235 ms)
✕ should warn during next start with output standalone (60029 ms)
● app dir - with output export (next start) › production mode › should warn during next start with output standalone
thrown: "Exceeded timeout of 60000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."
46 |
47 | // TODO: Move this test to test/production to run in isolation.
> 48 | ;(process.env.TURBOPACK_BUILD ? it.skip : it)(
| ^
49 | 'should warn during next start with output standalone',
50 | async () => {
51 | nextConfig.replace(`output: 'export'`, `output: 'standalone'`)
at integration/app-dir-export/test/start.test.ts:48:50
at integration/app-dir-export/test/start.test.ts:21:56
at Object.describe (integration/app-dir-export/test/start.test.ts:20:1)
```
This moves `experimental.cacheComponents` to a top level config. As part
of this, I disabled some tests in `build-output-prerender` that assert
on `cacheComponents` appearing in the experimental list. In a separate
PR, I'm going to show that Cache Components is enabled next to the
bundler info.
This also updates some docs pages to remove "experimental" language.
### What?
This PR introduces comprehensive type-aware parameter tracking and
fallback handling for dynamic routes in PPR scenarios, with enhanced
client-side parameter resolution capabilities.
### Why?
When using client segment cache with dynamic routes, Next.js faced
several critical issues that prevented reliable client-side navigation:
**1. Parameter Resolution Inconsistencies**: The previous system had
inconsistent behavior when handling parallel route parameters, causing
unpredictable parameter resolution across different route segments. This
led to client-side navigation failures when encountering dynamic route
segments that weren't statically generated.
**2. Limited Parameter Type Awareness**: The routing system lacked
semantic understanding of different parameter types (dynamic, catchall,
optional catchall), making it impossible to implement advanced parameter
handling features or provide proper fallback behavior for different
parameter scenarios.
**3. Incomplete Fallback Parameter Collection**: The original
`getFallbackRouteParams()` used simple string-based tracking that
couldn't capture the full complexity of parameter relationships,
especially in parallel route scenarios where parameters might be defined
at different levels of the route tree.
**4. Client-Side Parameter Extraction Gaps**: The `getDynamicParam`
function had limitations in handling fallback route parameters and
lacked proper encoding/decoding mechanisms, leading to potential
client-side errors during route resolution.
**5. Configuration Validation Missing**: There was no validation to
ensure `clientParamParsing` was only enabled when `clientSegmentCache`
was also enabled, leading to potential misconfigurations in production.
These issues became critical blockers for implementing reliable
client-side parameter parsing in PPR scenarios, particularly for the
Vercel platform integration
(https://github.com/vercel/vercel/pull/13740).
### How?
- Introduces `DynamicParamTypes` and `OpaqueFallbackRouteParams` for
structured, type-aware parameter tracking
- Refactors `getFallbackRouteParams()` to use route modules for
comprehensive parameter collection across the routing tree
- Adds `resolveParallelRouteParams` function to eliminate parallel route
parameter inconsistencies
- Enhances `getDynamicParam` with proper fallback parameter handling,
encoding, and comprehensive catchall support
- Implements configuration validation in `assignDefaultsAndValidate()`
to prevent misconfigurations
- Adds clientParamParsing boolean to the routes manifest configuration
(needed for https://github.com/vercel/vercel/pull/13740)
- Updates build pipeline to properly handle enhanced fallback parameters
across static generation, export, and rendering phases
NAR-305
---------
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
When a page component (or a parallel route slot) is cached, but other
parts of the component tree are dynamic, we generate a partially static
shell at buildtime that's resumed at runtime. During the resume, we need
to ensure that the cache key is the same as during prerendering. This is
currently not the case because we automatically include the (unused)
`searchParams` prop in the cache key. Its value is a hanging promise
during prerendering, and a resolving promise at runtime, leading to a
cache miss when looking up the cached page data in the Resume Data Cache
(RDC). Subsequently, hydration errors might occur when the recomputed
result is different than the prerendered result.
Since search params are not allowed to be read in public cache scopes
anyways, we can pass erroring search params into the cached page,
instead of using a hanging promise, and omit them from the generated
cache key. We already used this technique when `cacheComponents` is not
enabled.
closes NAR-87
## What?
Rename `experimental.dynamicIO` to `experimental.cacheComponents` across
the Next.js codebase.
## Why?
We're going to be merging the functionality of the `ppr`, `dynamicIO`
and `useCache` experimental flags into the singular `cacheComponents`
flag to reduce complexity of the codebase and simplify adoption for
users wanting to experiment with experimental features.
## How?
- Renamed the configuration option from `experimental.dynamicIO` to
`experimental.cacheComponents`
- Added deprecation handling with automatic migration for the old option
name
- Updated all documentation, tests, and internal references
- Updated Rust code in SWC transforms and Turbopack
- Maintained backward compatibility with deprecation warnings
NAR-158
When reading a cache entry, we usually check its tags against the recently revalidated tags, and dismiss it, if it has any of those tags. However, this is only needed during dynamic requests, specifically during the rendering that follows a revalidating server action.
During the prerender validation in dev mode, we should not discard the cache entries based on the pending revalidation. The concurrently running dynamic rendering will handle discarding and recreating those cache entries. During build-time prerendering, there will never be any pending revalidated tags.
This fixes a bug where a different value was rendered when revalidating with a server action and then reloading the page afterwards (or triggering another unrelated revalidating server action).
After having revalidated a prerendered route handler, on the subsequent request we must discard the prerendered data from the file system cache, as we already do for pages and fetch caches as well.
> [!NOTE]
> This PR is best reviewed with hidden whitespace changes.
We're splitting the test app routes between two root layouts, one for (partially) static pages that does not wrap its children in a Suspense boundary, and one for dynamic pages that adds a Suspense boundary above `body`, which opts those pages into fully dynamic rendering.
Two tests are expected to fail for now, because they revealed bugs related to revalidation, which will be fixed in follow-up PRs:
- https://github.com/vercel/next.js/pull/81611
- https://github.com/vercel/next.js/pull/81621
closes NAR-171
We want to respect the `__NEXT_EXPERIMENTAL_PPR` and `__NEXT_EXPERIMENTAL_CACHE_COMPONENTS` environment variables in the test apps, so that we can run them locally with these features enabled without needing to modify the config files.
So we're removing the requirement that `__NEXT_TEST_MODE` needs to be set at the same time. Usually you don't want to set this variable locally because it also changes other behavior in a few places.
This really has no downside, because it was already possible for users to set both variables and affect the flags, and now this can be done with a single variable. It allows us to keep the noise out of the test app's next config files.
We're also moving the assignments from the default config into `enforceExperimentalFeatures` to get the nice output when printing the experimental features in the CLI.
### What?
Introduces a testing infrastructure to validate Next.js behavior with
the experimental Cache Components feature (`dynamicIO`) enabled. This PR
adds dedicated CI jobs and configuration to run a subset of existing
tests with Cache Components activated.
This has 271 more disabled test suites than the Legacy PPR test suite
manifest. The expectation is that we will be resolving the test failures
in followup PRs.
### Why?
Cache Components (dynamicIO) represents a significant architectural
change in how Next.js handles component caching and rendering. To ensure
a smooth transition when this becomes the default behavior, we need to:
1. **Validate compatibility** - Ensure existing applications continue to
work correctly when Cache Components is enabled
2. **Identify edge cases** - Catch potential issues early by running
tests in this new mode
3. **Build confidence** - Provide a safety net for the eventual
migration to Cache Components as the default
The manifest-based approach allows us to incrementally expand test
coverage as we fix compatibility issues, rather than attempting to run
all tests at once which would be overwhelming and block progress.
### How?
- **Test manifest** (`test/cache-components-tests-manifest.json`):
Defines which tests to run and tracks known failures that need
addressing
- **CI workflow changes**: Adds three new job groups that mirror
existing test jobs but with Cache Components enabled:
- `test-cache-components-integration`
- `test-cache-components-dev` (parallelized across 6 groups)
- `test-cache-components-prod` (parallelized across 7 groups)
- **Conditional feature activation**: When
`__NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true` is set during testing,
`dynamicIO` is automatically enabled unless explicitly disabled
- **Test adjustments**: Updates error message expectations in tests to
handle the different environment label ("Prerender" vs "Server") when
Cache Components is active
The infrastructure is designed to be temporary and will be removed once
Cache Components becomes the default behavior.
NAR-172