Skip to content

Optimistic routing: client-side route prediction#88965

Merged
acdlite merged 1 commit intovercel:canaryfrom
acdlite:optimistic-routing
Jan 29, 2026
Merged

Optimistic routing: client-side route prediction#88965
acdlite merged 1 commit intovercel:canaryfrom
acdlite:optimistic-routing

Conversation

@acdlite
Copy link
Contributor

@acdlite acdlite commented Jan 23, 2026

Based on:


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.

@nextjs-bot
Copy link
Collaborator

nextjs-bot commented Jan 23, 2026

Tests Passed

initialFlightData: initialRSCPayload.f,
initialCanonicalUrlParts: initialRSCPayload.c,
initialRenderedSearch: initialRSCPayload.q,
initialCouldBeIntercepted: initialRSCPayload.i,
Copy link
Contributor

@vercel vercel bot Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong parameters passed to discoverKnownRoute() - isPPREnabled parameter receives incorrect values (initialPrerendered, isPrerender, prerendered) instead of false (PPR status unavailable from response headers)

Fix on Vercel

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. The prerendered field (also known as S in the RSC payload) indicates whether the response was prerendered, which is exactly the information we need to determine isPPREnabled for this particular route.

For server actions specifically, we use isPrerender because action responses can be static (when the action redirects to a prerendered page). The naming difference reflects the context: prerendered is the RSC payload field name, while isPPREnabled is what the known route system uses to track whether PPR applies to that route pattern.

size: 0,
staleAt: template.staleAt,
version: template.version,
}
Copy link
Contributor

@vercel vercel bot Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synthetic entries from route predictions can prevent fresh template discovery, causing future predictions to use stale route structure after server changes.

Fix on Vercel

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is working as designed. The synthetic entries are intentionally created to enable instant loading states for unprefetched routes that match a known pattern.

When we navigate to an unprefetched route (e.g., /blog/post-2 with prefetch={false}), the synthetic entry allows us to immediately show the loading boundary with the correct params ("Loading post-2...") before any server response arrives. This is the core user-facing benefit of route prediction.

The actual segment data will be fetched during navigation and will replace the synthetic entry's template. The renderedSegments field in the synthetic entry is empty, so segment cache lookups will miss and trigger fresh fetches. The template itself (route structure, loading boundaries) is reused since it's the same for all param values of the same route pattern.

@acdlite acdlite force-pushed the optimistic-routing branch 7 times, most recently from a036b75 to 43903b0 Compare January 25, 2026 02:47
@acdlite acdlite marked this pull request as ready for review January 25, 2026 17:42
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.
@acdlite acdlite force-pushed the optimistic-routing branch from 43903b0 to 9f45cb3 Compare January 29, 2026 22:05
@acdlite acdlite merged commit f17694d into vercel:canary Jan 29, 2026
8 checks passed
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 13, 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