Commit Graph
10 Commits
Author SHA1 Message Date
Andrew ClarkandGitHub 3de2d1a213 Unify allow-runtime with Partial Prefetching (#96106)
Removes the "allow-runtime" prefetch config, and turns its behavior on
implicitly wherever Partial Prefetching is enabled.

The original motivation for "allow-runtime" was to give apps more
control over server costs triggered by prefetches. Until a route
explicitly opts in, prefetches would only be served from the CDN, not
from the server. The problem, though, was it was very confusing to know
when to add or remove this configuration. The incentive for many apps
was to add it everywhere, with no clear signal for when to remove it.

Our updated thinking is that Partial Prefetching itself already provides
sufficient protection against runaway prefetching costs: per-link
prefetches only happen on Link components that explicitly opt in with
the prefetch prop.

The optimizations landed earlier in this stack also make allow-runtime
less necessary: on pages where all the content is statically renderable,
prefetches are served from the static cache and no runtime request is
ever issued; only a page that accesses non-static data is prefetched at
runtime.

The upshot of this decision is that runtime versus static becomes an
internal optimization; the same content gets prefetched regardless of
whether or how Next.js is able to optimize it.
2026-07-28 11:51:29 -04:00
Janka UrygaandGitHub c131314bcf [PP] Validate Shell prefetches (except gSP) (#95151)
Implements instant validation for `partialPrefetching`. In this mode,
`<Link>` prefetches an App Shell, which cannot access link data, and we
need to warn for that.

The changes in `instant-validation.tsx` are relatively simple: for an
App Shell, we simply use `ShellRuntime` for all the new segments. We
might also force them into `Runtime` for the purposes of discriminating
dynamic holes. If a hole is present in `ShellRuntime` but disappears in
`Runtime`, then we know it's caused by **link data** (as opposed to
runtime or dynamic data). I've added some new error messages for this
case.

Note that the implementation here is incomplete: it uses the chunks from
the dev render, which resolves static params in the `Static` stage. We
use `ShellRuntime` for validating the App Shell, so as a result, static
params are incorrectly included in it and don't trigger link data
errors. This will be implemented in a follow-up.

Note: It seems like we have some pre-existing bug in build validation
where `fallbackParams` aren't populated, so params resolve statically
when they shouldn't. I've marked two tests with `// TODO(app-shells):
missing fallback params in build validation` so we can follow up and fix
those.
2026-07-01 02:36:41 +00:00
Josh StoryandGitHub d424aa8ec4 Stabilize unstable_instant (#94578)
This API has experimental modes for build validation but the dev
validation is going to be stable in the next release.
2026-06-09 13:50:25 -07:00
Josh StoryandGitHub adb2913661 Stabilize export const prefetch (#94571)
This API is going to ship stable but some options may individually be
marked unstable
2026-06-08 18:30:55 -07:00
Josh StoryandGitHub a9539183b8 Rename prefetch option force-runtime to allow-runtime (#94568)
While force-runtime does opt you into runtime prefetching today (i.e. it
does force) the intended semantic is shifting to convey that the Segment
itself is designed for and makes sense (i.e. cost / performance
tradeoff) to runtime prefetch. In the future segments that may be
runtime prefetched might not be for various optimization reasons. We
therefore are renaming the option from force-runtime to allow-runtime.
In the future if we change allow-runtime to sometimes not runtime
prefetch we can always ship a new force-runtime as a codemod option that
recovers the current behavior.

This wording also better demonstrates why this is a feature of the
Segment and not say an option on the link like `<Link
prefetch="force-runtime" />`.
2026-06-08 17:02:16 -07:00
Josh StoryandGitHub d86e197728 Support configuring a default instant validation level (#93301)
Adds `experimental.instantInsights.validationLevel` so an app can opt
into instant validation for all routes by default.

There are two public levels
* `manual-warning`: currently the default. this mode won't enable
instant validation except where you add `export const unstable_instant =
true` into your Layouts, Pages, and Default files
* `warning`: This mode will enable instant validation for all Page and
Default files. You can override this with `export const unstable_instant
= false` in Layouts, Pages, and Default files to tune what navigations
are validated.

In addition this change disables build time validation.

There is a now undocumented way to get back to running build validation
with
* `experimental-error`
* `experimental-manual-error`

The build validation isn't ready for users because we need to overhaul
the setup where you provide cookies and other sample values so we can
run validation without a live session like we do in dev.

To further support this the two remaining object properties that are
also not ready for active use, `samples` and `from` are being updated to
`unstable_samples` and `unstable_from` respectively.

We will continue to iterate on build time validation but may remove
these APIs or alter their semantics as we figure out a more ergonomic
way to support this setup.

In anticipation of multiple levels the object form of `instant` can be
provided a level value `warning` or `experimental-error`. this latter
option is similarly undocumented so documented behavior will only cover
`warning` which is generally a synonym of true unless you are activating
error by default
2026-05-06 10:37:50 -07:00
Josh StoryandGitHub 1931877c29 Remove prefetch from instant config (#92761)
prefetching is now controlled with the prefetch export and this option
is inert on the instant export. this removes the prefetch option as a
valid property on the instant config type.

also updates the ts plugin for this export and adds the definition for
the previously landed prefetch export
2026-04-17 10:44:45 -07:00
Josh StoryandGitHub a7d1573391 Support more prefetch configuration options (#92859)
"auto" mode is the default. It does not need to be explicitly exported
"force-" modes suggest overriding framework heuristics "force-disabled"
disables prefetching for this segment "force-static" forces any
prefetching of this segment to be static "force-runtime" forces any
prefetching of this segment to do runtime prefetching

It's worth noting that when runtime prefetching we fetch the necessary
segment and all child segments in a single request. This means that a
deeper segment might specify disabled or static and still get
conditionally rendered as a runtime prefetch. This was already the
behavior of runtime prefetching and not changing in this PR just
something to call out since it may be confusing to folks trying to
understand how the implementation of prefetching actually work
2026-04-17 09:28:54 -07:00
Josh StoryandGitHub ac6d9932a6 Add unstable_prefetch route segment config and decouple runtime prefetching from unstable_instant (#92754)
Allows you to opt into runtime prefetching without coupling it to the
way you configure instant validation. We do not intend to allow shipping
runtime prefetching without opting into instant validation unless you
specifically disable the validation because the cost of the runtime
prefetching is potentially high but the way you configure validation is
likely going to be pulling in lots of test code and we want to make it
easier to discern that the validation code is not going to be bundled
into production builds so separating the config is helpful.

Another reason to separate the config is that we expect that eventually
most prefetching is configured globally and you do not need to opt
specific segments into a different prefetching strategy.

Improved client component error messages to accurately describe the
constraint: these are route segment configs that require a Server
Component module, not exports that are forbidden.
2026-04-14 07:58:36 -07:00
Janka UrygaandGitHub 7bbea9d4b1 [Instant] Build-time validation (#90964)
Initial version of build-time instant validation.

During prerendering, if we have `unstable_instant` configs, we perform a
complete staged render of the page (including dynamic data). This render
mocks request data (`params`/`searchParams`/`cookies`/`headers`) using
the `samples` provided in `unstable_instant`. Then, we use the staged
chunks to run instant validation in mostly the same way that we would in
dev. Most changes in the validation flow are related to samples, which
also need to be passed into the client render to mock APIs like
`useParams`.

### Providing Mock data

We run everything in a fresh workStore+workUnitStore to (hopefully)
avoid any data from the prerender from sneaking in. This is quite
involved.

A big part of the trickiness is that if you do e.g. `(await
cookies()).get('myCookie')` but the `samples` don't contain `myCookie`,
we want the validation to error and require explicitly saying what state
`myCookie` is in.
If it's available, use
```ts
export const unstable_instant = {
  // ...
  samples: { cookies: [{ name: "myCookie", value: "someValue" }] }
}
```
If it's not available, use
```ts
export const unstable_instant = {
  // ...
  samples: { cookies: [{ name: "myCookie", value: null }] }
}
```
This explicitness makes it harder to e.g. unintentionally validate a
logged-out state.

Note that even validating a `prefetch: 'static'` page might require
samples, because we need the shared parent for the navigation to be
(mostly) complete. We might be able to relax this in the future, but
it's not straightforward.

Another issue is that a segment might access a missing sample in a
"benign" way, i.e. where it wouldn't block validation (or be inside a
suspense boundary). Ideally we wouldn't require providing a sample for
usages like that, but currently we do.

### Instrumenting APIs to detect missing samples

All request APIs are instrumented in this way.
- `params` throw when accessing a property that was not declared in
`samples`. same for the object from `useParams()`. `in` is allowed
because we know the shape of the params object statically, based on the
routing structure.
- `searchParams` throw when accessing a property that was not declared
in `samples` and `in` checks.
- objects returned from `cookies()`, `headers()`, `useSearchPrams()`
throw on `get()` and `has()`.

There's a lot of tests for this behavior in `instant-validation-build`.

One gap is that iteration/enumeration is currently not instrumented.
We're punting on it for now and will revisit in a follow-up.

### Relation to prerender

We kick off the validation after the prerender, in
`renderToHTMLOrFlightImpl`. Ideally, we should kick this off somewhere
higher up, separately from the prerender itself, but we're leaving that
for future work.

Note that if we do multiple prerenders for the same page (e.g. due to
`generateStaticParams`, we'll only run validation for the first
prerender for that route. `generateStaticParams` is not currently
integrated into the validation at all, i.e. we don't use those params to
validate, so this is safe. All params need to be provided via `samples`.
This will likely change in the future, but we're not sure how yet.

### Disabling build validation

Build-time instant validation is experimental, and has more potential
problems to deal with than dev validation. To allow incremental rollout
and testing, we allow disabling it per instant config:
```ts
export const unstable_instant = {
  unstable_disableBuildValidation: true
  // ...
}
```

for parity, there's also `unstable_disableBuildValidation`, but we don't
expect that to see much use.
2026-03-12 14:57:38 +01:00