Node.js ships with a built-in `fetch` now so `node-fetch` is no longer
necessary. Mostly motivated by tracing Node.js deprecation warnings
which originated from `node-fetch` by calling the deprecated
`url.parse`.
Call sites keep working through a compatibility type on `fetchViaHTTP`
that translates node-fetch-only options: Instead of `agent` we pass to
`http(s)` directly, `timeout` becomes `AbortSignal.timeout`, and Node.js
readable streams are accepted as bodies with `duplex: 'half'` set
automatically.
The `abort-controller` polyfill is dropped since its signal type
predates the current AbortSignal and undici would not honor it.
`node-fetch` stays installed because `scripts/generate-release-log.mjs`,
`scripts/reset-project.mjs`, and `scripts/update-google-fonts.js` still
import it (follow-up material). Fixture apps will be migrated
separately.
`experimental.useExperimentalReact` names the React *build* it pulls in
rather than the feature it's there for. Rename it to `blockingSSR` — the
actual capability (React's blocking SSR, which emits `<link
rel="expect">` to hold first paint until the streamed shell is coherent)
— mirroring React's own feature flag so it's clear why the experimental
channel got enabled when grepping.
<!-- NEXT_JS_LLM_PR -->
Opting into React's experimental channel — which emits `<link
rel="expect">` to hold first paint until the streamed shell is coherent,
avoiding flicker from partially-streamed HTML — currently requires
enabling an unrelated feature like `experimental.taint` as a side
effect, which is confusing. This adds
`experimental.useExperimentalReact` as a direct opt-in.
It feeds the existing `needsExperimentalReact` aggregation and the
matching Turbopack `react_channel` switch, selecting the
`react@experimental` build the same way `taint`, `transitionIndicator`,
and `gestureTransition` do. It's opt-in only: an explicit `false` can't
disable the channel when one of those still requires it (the taint APIs
only exist in the experimental build), so `assignDefaults` warns on that
contradiction. Covered by a webpack e2e test mirroring the existing
`taint` channel test.
<!-- NEXT_JS_LLM_PR -->
*This doesn't enable skew protection for any new tests, only preparation*
Prepare a lot of test assertions so that they work correctly when a `?dpl=123` query param is present:
- some regexes should accept the query
- some places assumed that the `<script>`'s `src` is a valid pathname, whereas it might have a query param that has to be stripped before removing the path
- some hardcoded build ids instead of `next.buildId`
Closes PACK-6539
When `experimental.runtimeServerDeploymentId` is enabled, read the `process.env.NEXT_DEPLOYMENT_ID` and append to the chunks when evaluating the `client-reference-manifest.js`
Originally, I tried to do it after the manifest is loaded (in `loadComponents` or in the `RouteModule.prepare`) to "hydrate" the manifest, but that is too late because the manifest needs to be initialized immediately to make module-evaluation-time server actions work (which need the manifest immediately)
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?
Adds build-time validation to require explicit `default.js` files for
all parallel route slots (except the implicit "children" slot). This
validation is implemented in both Webpack and Turbopack bundlers.
### Why?
Parallel routes without `default.js` files currently cause silent 404
errors when users navigate to those routes. This creates confusion and
hard-to-debug issues because the routes appear to be configured
correctly but fail at runtime without any indication of what went wrong.
By making this validation explicit at build time, developers get
immediate feedback about missing required files with clear error
messages and documentation links, catching configuration mistakes before
deployment.
### How?
**Rust/Turbopack** (`crates/next-core/src/app_structure.rs`): Added
`MissingDefaultParallelRouteIssue` that emits a build error when a
parallel route slot is missing its `default.js` file. The validation is
skipped for the "children" slot since it's implicit and doesn't require
a default file.
**Webpack**
(`packages/next/src/build/webpack/loaders/next-app-loader/index.ts`):
Added validation that throws `MissingDefaultParallelRouteError` when
`default.js` cannot be resolved. The "children" slot falls back to the
existing `PARALLEL_ROUTE_DEFAULT_PATH` behavior for backward
compatibility.
**Error Class**
(`packages/next/src/shared/lib/errors/missing-default-parallel-route-error.ts`):
New error type with helpful messaging that includes the slot path,
explanation of the requirement, and a link to documentation.
**Migration Path**: Users who want the previous 404 behavior can
explicitly create a `default.js` that calls `notFound()`, or return
`null` for empty slots:
```tsx
import { notFound } from 'next/navigation'
export default function Default() {
notFound()
}
```
Users can also run the following Deno script to generate the default
files for them:
https://gist.github.com/wyattjoh/ba7263ecb637ef399d3e3e4db63ffbd6
**Breaking Change**: This is a breaking change timed for Next.js 16
beta. Builds will now fail if parallel route slots are missing required
`default.js` files.
This hard deprecates the `experimental.ppr` configuration, requiring
users to opt-in instead via `experimental.cacheComponents`. This does
mean that the previous `experimental.ppr = "incremental"` will no longer
be supported.
NAR-433
Enabling `experimental.isolatedDevBuild` required many changes to the
current workflow, so we will incrementally roll out to the tests.
Enabling on test-dev instead of test-experimental-dev because
`-experimental` CIs are filtered via `experimental-tests-manifest.json`
and they don't cover all tests. We want to enable this feature by
default so we should ensure this incremental rollout is covered on all
test cases.
The flag was enabled for `test-experimental-dev` at
https://github.com/vercel/next.js/pull/84099, and this PR moves the flag
to the `test-dev` job.
1. ~~test-experimental-dev
([link](https://github.com/vercel/next.js/pull/84099))~~
2. test-dev (here)
3. test-prod
4. test-integration
5. test-unit
6. Enable by default, remove the flag, and update the rest
x-ref: https://github.com/vercel/next.js/pull/84043
Fixes edge runtime pages with dynamic routes returning 500 errors when
the URL segment literally contains bracket syntax that matches the
folder name (e.g., accessing `/[id]` for a route defined as
`/[id]/page.tsx`).
The removal of web-server.ts in #81389 modified how route params were
extracted for dynamic routes. The route matcher in prepare() was being
created with the full internal page path (e.g., /[id]/page) which
generated a regex expecting /page at the end. However, actual URL
pathnames don't include the /page suffix, causing the matcher to fail
when trying to extract params. This issue specifically manifested when
the URL contained literal brackets (like /[id]), as the fallback param
extraction would fail and leave params empty, leading to the error.
This PR normalizes the page path using before creating the route matcher
in prepare(). This ensures the regex pattern matches the actual URL
pathname format (without /page suffix).
Fixes NEXT-4698
we define most of the app router headers with capital letters, like
this:
```ts
export const NEXT_ROUTER_PREFETCH_HEADER = 'Next-Router-Prefetch' as const
```
which means we have to keep lowercasing the header everywhere when
checking for its presence, because node and other intermediate layers
normalize all headers to lowercase:
```ts
if (req.headers[NEXT_ROUTER_PREFETCH_HEADER.toLowerCase()]) { ... }
```
this seems pretty pointless -- we can just define the headers in
lowercase form, and skip all those `toLowerCase()` calls.
(all the above sentences have been normalized to lowercase to match the
spirit of this pr)
For all RSC requests, we require the `_rsc` search param to match the
hash of its corresponding RSC headers. In the case where the match
fails, we respond with a redirect to the correct search param. This hash
check only applies to `next start` mode for now.
Some tests in `rsc-basic` asserts that certain nodes should be empty,
but streaming metadata can get in there and break that expectation.
- i switched the test to use a full browser. this is better than
checking the raw html, because then react gets rid of any `<template>`
tags inside the div (which are used for streaming SSR things)
- we're also now only asserting that the inner text of the node should
be empty, which avoids failures because of a `<div hidden>` inserted by
streaming metadata. this is a slightly weaker assertion than "this node
has no children at all" but it should be enough for our purposes here.
also reverts #78449 and #78424, they were meant for debugging this issue
follow up to #78424. annoyingly, it appears that the console.log output
gets swallowed by jest somehow, so i'm using `require('console')`
instead which jest doesn't instrument.
rip. you served a purpose once, but now you're just getting in the way. this improves typesafety quite a bit, because `BrowserInterface` had a whole bunch of random `any`s everywhere
also
- removes `evalAsync`. no idea why that was needed, but we're happily using promises in normal eval, so it can be dropped
- adds more safety to `chain`
I've been seeing this test flake with some regularity:
---
● app dir - rsc basics › should be able to navigate between rsc routes
```
request.allHeaders: Target page, context or browser has been closed
168 | page.on('request', (request) => {
169 | requestsCount++
> 170 | return request.allHeaders().then((headers) => {
| ^
171 | if (
172 | headers['RSC'.toLowerCase()] === '1' &&
173 | // Prefetches also include `RSC`
at Page.allHeaders (e2e/app-dir/rsc-basic/rsc-basic.test.ts:170:26)
```
---
[Example test run
here](https://github.com/vercel/next.js/actions/runs/14313872710/job/40115445718?pr=77898#step:33:3431)
The error is actually unrelated to `should be able to navigate between
rsc routes`, and is coming from async operations (that haven't completed
in time) in another test in the same suite, `should reuse the inline
flight response without sending extra requests` .
I've de-asyncified the test's `page.on('request')` handler -- we don't
need the async `allHeaders()`, `headers()` is enough.
(I verified this by adding a client component that manually sends a
request with `RSC: 1`, and that does indeed trigger our request
listener).
I've also added a `waitForIdleNetwork` to hopefully make it complete all
requests before the test ends.
- `newDevOverlay: true` by default (enables experimental React builds on
canary until owner stacks progress further)
- `run-tests` now sets the env var for tests that were relying on it for
forking behavior
- PPR runners now run with the flag disabled to help catch regressions
in the old overlay until we remove it
- Fixed a number of tests that had outdated snapshots or missed forking
behavior because they weren't running in CI
- Disabled a test that was failing in Turbopack + Experimental React
that is unrelated to the overlay (see:
https://github.com/vercel/next.js/pull/75989)
---------
Co-authored-by: devjiwonchoi <devjiwonchoi@gmail.com>
This enhances the current parsing completed by Next.js of configuration
from app segments. Previously a collection of fragile checks was used to
parse the different segment configuration options which performed no
validation on the inputs.
This uses the `zod` library (which we already use internally) to perform
validation on the configuration exported. A followup PR #70480 will add
more verbose error logging around the validation errors.
Next.js has a number of dynamic APIs that aren't available during
prerendering. What happens when you access these APIs might differ
depending on the mode you are using, for instance if you have PPR turned
on or the newly introduced `dynamicIO` experimental mode. But regardless
of the mode the underlying API represents accessing something that might
only be available at render time (dynamic rendering) rather than
prerender time (build and revalidate rendering)
Unfortunately our current dynamic APIs make certain kinds of modeling
tricky because they are all synchronous. For instance if we wanted to
add a feature to Next.js where we started a dynamic render before a
Request even hits the server it would be interesting to be able to start
working on everything that does not rely on any dynamic data and then
once a real Request arrives we can continue the render and provide the
associated Request context through our dynamic APIs.
If our dynamic APIs were all async we could build something like this
because they represnt a value that will eventually resolve to some
Request value. This PR updates most existing dynamic APIs to be async
rather than sync. This is a breaking change and will need to be paired
with codemods to realistically adopt. Additionally since this change is
so invasive I have implemented it in a way to maximize backward
compatibility by still allowing most synchronous access. The combination
of codemods, typescript updates, and backward compat functionality
should make it possible for projects to upgrade to the latest version
with minimal effort and then follow up with a complete conversion over
time.
#### `cookies()`
`cookies()` now returns `Promise<ReadonlyRequestCookies>`. Synchronous
access to the underlying RequestCookies object is still supported to
facilitate migration.
```tsx
// ------------ preferred usage
// async Server Component
const token = (await cookies()).get('token')
// sync Server Component
import { use } from 'react'
//...
const token = use(cookies()).get('token')
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
const token = cookies().get('token')
// typescript, dev warning at runtime
import { type UnsafeUnwrappedCookies } from 'next/headers'
// ...
const token = (cookies() as unknown as UnsafeUnwrappedCookies).get('token')
```
#### `headers()`
`headers()` now returns `Promise<ReadonlyHeaders>`. Synchronous access
to the underlying Headers object is still supported to facilitate
migration.
```tsx
// ------------ preferred usage
// async Server Component
const header = (await headers()).get('x-foo')
// sync Server Component
import { use } from 'react'
//...
const header = use(headers()).get('x-foo')
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
const header = headers().get('x-foo')
// typescript, dev warning at runtime
import { type UnsafeUnwrappedHeaders } from 'next/headers'
// ...
const header = (headers() as unknown as UnsafeUnwrappedHeaders).get('x-foo')
```
#### `draftMode()`
`draftMode()` now returns `Promise<DraftMode>`. Synchronous access to
the underlying DraftMode object is still supported to facilitate
migration.
```tsx
// ------------ preferred usage
// async Server Component
if ((await draftMode()).isEnabled) { ... }
// sync Server Component
import { use } from 'react'
//...
if (use(draftMode()).isEnabled) { ... }
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
if (draftMode().isEnabled) { ... }
// typescript, dev warning at runtime
import { type UnsafeUnwrappedDraftMode} from 'next/headers'
// ...
if ((draftMode() as unknown as UnsafeUnwrappedDraftMode).isEnabled) { ... }
```
#### `searchParams`
`searchParams` is now a `Promise<{...}>`. Synchronous access to the
underlying search params is still supported to facilitate migration.
```tsx
// ------------ preferred usage
// async Page Component
export default async function Page({
searchParams
}: {
searchParams: Promise<{ foo: string }>
}) {
const fooSearchParam = (await searchParams).foo
}
// sync Page Component
import { use } from 'react'
export default function Page({
searchParams
}: {
searchParams: Promise<{ foo: string }>
}) {
const fooSearchParam = use(searchParams).foo
}
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
export default async function Page({ searchParams}) {
const fooSearchParam = searchParams.foo
}
// typescript, dev warning at runtime
import { type UnsafeUnwrappedSearchParams } from 'next/server'
export default async function Page({
searchParams
}: {
searchParams: Promise<{ foo: string }>
}) {
const syncSearchParams = (searchParams as unknown as UnsafeUnwrappedSearchParams<typeof searchParams>)
const fooSearchParam = syncSearchParams.foo
}
```
#### `params`
`params` is now a `Promise<{...}>`. Synchronous access to the underlying
params is still supported to facilitate migration. It should be noted
that while params are not usually dynamic there are certain modes where
they can be such as fallback prerenders for PPR.
```tsx
// ------------ preferred usage
// async Segment Component
export default async function Layout({
params
}: {
params: Promise<{ foo: string }>
}) {
const fooParam = (await params).foo
}
// sync Segment Component
import { use } from 'react'
export default function Layout({
params
}: {
params: Promise<{ foo: string }>
}) {
const fooParam = use(params).foo
}
// ------------ temporarily allowed usage
// javascript, dev warning at runtime
export default async function Layout({ params}) {
const fooParam = params.foo
}
// typescript, dev warning at runtime
import { type UnsafeUnwrappedParams } from 'next/headers'
export default async function Layout({
params
}: {
params: Promise<{ foo: string }>
}) {
const syncParams = (params as unknown as UnsafeUnwrappedParams<typeof params>)
const fooSearchParam = syncParams.foo
}
```
### Typescript Changes
When using typescript with Next.js currently it is up to you to author
types for Pages, Layouts and other Segment components that recieve props
like `params` and `searchParams`.
Next comes with some build-time type checking to ensure you have not
improperly typed various top level module exports however the current
type assertions for `params` and `searchParams` is `any`. This isn't
very helpful because it allows you to erroneously type these props.
`searchParams` is tricky because while the default type is a dictionary
object parsed using node.js url parsing it is possible to customize when
running a custom Next.js server. However we can ensure that you
correctly type the prop as a Promise so with this change the validated
type for `searchParams` will be `Promise<any>`.
In the long run we will look at updating the `searchParams` underlying
type to be URLSearchParams so we can move away from supporting
customized parsing during rendering and we can get even more explicit
about valid types.
`params` is more straight forward because the framework controls the
actual `params` prop implementation and no customization is possible. In
the long run we want to enforce you are only typing params that are
valid for the Layout level your file is located in but for now we are
updating the allowed type to be `Promise<{[key: string]: string |
string[] | undefined }>`.
These new type restrictions may also require fixes before being able to
successfully build a project that updates to include these breaking
changes. These changes will also not always be codemodable because it is
valid to type the entire component using an opaque type like `Props`
which our codemods may not have an ability to introspect or modify.
Pages router (`/pages`) will continue to support React 18 not the React
19 RC. Current thinking is that we'll add support for React 19 in Pages
Router once 19 is stable.
This does not affect App Router (`/app`) which continues to use the
latest React Canary (i.e. React 19).
https://github.com/vercel/next.js/pull/65058 is required reading to
understand the changes in this PR
---------
Co-authored-by: Zack Tanner <1939140+ztanner@users.noreply.github.com>
### What
When statically generating a page, Next.js will attempt to render the
application tree twice, resulting in unexpected calls to upstream APIs
and also slowing down the application during build as it needs to make 2
passes for the same data.
### Why
Next.js currently calls React's `renderToReadableStream` API in two
spots: once for the HTML render, and again for the Flight render. This
is because we want to generate a `.rsc` output for statically generated
pages, so that on navigation, we don't need to call the server to
retrieve the flight payload.
### How
Rather than calling `renderToReadableStream` twice, this refactors the
approach to instead only perform it once. This introduces a new type to
describe the response from calls to `createFromFetch`: `RSCPayload`,
including a set of utils to access the relevant payload data, as it can
change depending on calling context.
- During the initial SSR response & when statically generating the
flight payload, this will be an object containing the top-level props
that are used to render `<AppRouter />`
- For dynamic pages, this will only return the diffed component tree,
which is computed by `walkTreeWithFlightRouterState` traversing the
provided `next-router-state-tree` (from the client) to determine where
to start rendering (**this is unchanged from today**)
- For server actions, this will return the action result, in addition to
the same payload as above (**this is also unchanged**)
Fixes#58736Fixes#43254Fixes#60562
Closes NDX-17
### What
Add new test case where a named export from client component is being
exported as page
### Why
We found this case while investigating the errors triggered introduced
by #66286 , adding this test to avoid future regression
### What
Fix a bug introduced in #65694 , use app-page runtime for app router
layers
### Why
This is basically reverted the route context picking up logic we had
before.
During the test we found the error thrown
> Module not found: shared-runtime module router-context cannot be used
in rsc layer
Which is caused by a `next/router` imports in rsc page. Decided to
revert to what it was before as the most safe way to load share module
contexts.
It's caused by `next-contentlayer` usage that they're using
`next/router` in server component MDX, but we cannot lint error that
from node_modules. (We actually can, but disabled that due to various
mis-usage of server/client hooks we had before)
This PR promotes and renames experimental configuration options related
to server bundling:
- `serverComponentsExternalPackages` -> `serverExternalPackages`
- `bundlePagesExternals` -> `bundlePagesRouterDependencies`
Existing docs for `serverComponentsExternalPackages` was changed.
New docs for `bundlePagesRouterDependencies` were added.
Closes NEXT-3332
# Turbopack
* https://github.com/vercel/turbo/pull/7027 <!-- Donny/강동윤 - Update `swc_core` to `v0.87.28` -->
---
### What?
Update swc crates
### Why?
Required for #57718.
`styled-jsx` crate now has a hook to transform CSS code using a
Rust-side API
### How?
Fixes#57718
Closes PACK-2256
In #59725 I skipped this test in PPR prod mode, but not dev because CI
wasn't failing for dev. The idea was to investigate the failure
post-merge because it wasn't block-worthy.
But the test did fail in dev mode when CI ran on canary. So this updates
the guard to skip in dev, too.
Will follow up with a PR to fix the test itself.
Closes NEXT-1913
Apply react-server condition and related API checks for pages API.
if you're doing react SSR with renderToString in middleware it should be disallowed. Imaging it could send the rendered html code to client and you display it in browser. But it might require hydration so it can be broken.
Follow up for #57448 , same reason explained in #57448
Closes NEXT-1653