Skip to content

Decouple route stale time from segment-level data#88834

Merged
acdlite merged 1 commit intovercel:canaryfrom
acdlite:segment-stale-time
Jan 28, 2026
Merged

Decouple route stale time from segment-level data#88834
acdlite merged 1 commit intovercel:canaryfrom
acdlite:segment-stale-time

Conversation

@acdlite
Copy link
Contributor

@acdlite acdlite commented Jan 21, 2026

Summary

Refactors segment cache entries to receive their stale time from the server response rather than inheriting from the parent route cache entry. This decouples the lifetime of segment data from the route tree, preparing for future optimizations.

When I say "route" here, what I'm referring to is the mapping of URL -> layout/page hierarchy. Typically this is a predictable mapping based on the App Router's file-based routing conventions; however, it's possible to rewrite the URL to a different internal route path.

Conceptually, the cache life of a route corresponds to how often a proxy is expected to dynamically rewrite or redirect a URL. We don't currently expose a way to directly control this, but if we did, you could think of it as being equivalent to adding use cache (and cacheLife, cacheTag, etc.) to the proxy() function.

The point, though, is that the route tree has a different lifetime than data rendered on the page. Route trees change relatively infrequently, like due to a logged in/out state change, or a feature flag change. So we can/should cache them fairly aggressively by default.

This is related to an upcoming change where we'll go even further and predict routes we haven't visited yet based on structurally similar routes we've already seen.

This PR itself, though, makes no behavioral changes; it just sets up the subsequent steps.

Test Plan

No behavioral changes - existing tests should pass.

@acdlite acdlite changed the title Add per-segment staleTime to prefetch protocol Decouple route stale time from segment-level data Jan 21, 2026
@acdlite acdlite force-pushed the segment-stale-time branch from 53a6238 to cc865ed Compare January 21, 2026 01:54
@nextjs-bot
Copy link
Collaborator

nextjs-bot commented Jan 21, 2026

Tests Passed

@acdlite acdlite marked this pull request as ready for review January 21, 2026 03:45
@acdlite acdlite requested a review from ztanner January 21, 2026 03:46
@acdlite acdlite force-pushed the segment-stale-time branch from cc865ed to 2aa6696 Compare January 24, 2026 17:03
Refactors segment cache entries to receive their stale time from the
server response rather than inheriting from the parent route cache
entry. This decouples the lifetime of segment data from the route tree,
preparing for future optimizations.

When I say "route" here, what I'm referring to is the mapping of URL ->
layout/ page hierarchy. Typically this is a predictable mapping based on
the App Router's file-based routing conventions; however, it's possible
to rewrite the URL to a different internal route path.

Conceptually, the cache life of a route corresponds to how often a proxy
is expected to dynamically rewrite or redirect a URL. We don't currently
expose a way to directly control this, but if we did, you could think of
it as being equivalent to adding "use cache" (and cacheLife, cacheTag,
etc.) to the `proxy()` function.

The point, though, is that the route tree has a different lifetime than
data rendered on the page. Route trees change relatively infrequently,
like due to a logged in/out state change, or a feature flag change. So
we can/should cache them fairly aggressively by default.

This is related to an upcoming change where we'll go even further and
_predict_ routes we haven't visited yet based on structurally similar
routes we've already seen.

This PR itself, though, makes no behavioral changes; it just sets up
the subsequent steps.
@acdlite acdlite merged commit 3769252 into vercel:canary Jan 28, 2026
327 of 335 checks passed
acdlite added a commit that referenced this pull request Jan 28, 2026
Based on:

- #88834

---

Follows from the previous commits that decoupled route stale time from
segment data. Now that routes have their own lifecycle, we can simplify
the implementation: route stale times are always the static constant,
rather than being derived from server-provided values.

Route structure is essentially static — it only changes on deploy. The
exception is rewrites/redirects in middleware or proxy, which can change
routes dynamically based on request state. But we have other strategies
for handling those cases (e.g., route interception headers, cache
invalidation on auth state changes).

This simplification removes the need to thread stale time through
various call sites. The `fulfillRouteCacheEntry` function now computes
the stale time internally from a `now` timestamp parameter, following
the convention used elsewhere in the codebase.
acdlite added a commit that referenced this pull request Jan 29, 2026
Based on:

- #88834
- #88989

---

This implements a partial "vary params" optimization for the segment
cache. When a segment doesn't access params on the server, its
prefetched data can be reused across different param values.

Two cases are handled:
1. Segments without a user-provided layout (only loading.tsx or nothing)
2. Segments with a 'use client' layout

The server now includes a varyParams field in segment prefetch
responses. When varyParams is an empty Set, the client re-keys the cache
entry with Fallback for all param values, making it reusable across
different params.

This is a stepping stone toward full vary params tracking where Server
Components would track which specific params they access during
rendering.
acdlite added a commit that referenced this pull request Jan 29, 2026
Based on:

- #88834
- #88989
- #88998

---

Adds optimistic routing, enabling the client to predict route structure
for URLs that haven't been prefetched yet. When navigating to a URL like
/blog/post-2, if we've previously learned the pattern from /blog/post-1,
we can predict the route structure without waiting for a server
response.

The client learns route patterns from server responses and builds a trie
indexed by URL parts. When a cache miss occurs, we check if the URL
matches a known pattern. If so, we create a synthetic cache entry from
the template, allowing immediate rendering of loading boundaries.

Static siblings (like /blog/featured alongside /blog/[slug]) are tracked
to avoid incorrect predictions — we only predict dynamic routes when
we're confident no static sibling matches.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 12, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants