mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[GH-ISSUE #588] Client-side navigation with search params: server component receives stale/default params #128
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#128
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @NathanDrake2406 on GitHub (Mar 19, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/588
Migrating my movie site to vinext from OpenNext and while it's a lot faster, it's also buggy in the client-side navigation department. Specifically,
router.push()with search param changes causes the server component to re-render with stale or default params instead of the updated ones. This only happens in the production build (viawrangler devorvinext deploy).vinext devworks correctly.Repro
I have a
/toppage (server component) that readssearchParamsfor filtering (genre, provider, limit, etc.), and a client component that updates filters viauseRouter().push()insideuseTransition:The server component uses these params to query a database and render a filtered list:
What happens
Starting from
/top?limit=100&genre=Romance&provider=1899(HBO Max):router.push("/top?limit=100&genre=Romance&provider=8")./top?limit=100&genre=Romance&provider=8.Works in dev, broken in prod
This works perfectly in
vinext dev(Node). Filters update correctly, back button preserves history, no duplicate rendering. The bug only shows up in the production build viawrangler devorvinext deploy. So the issue is likely in the production RSC request handler or the bundled router's RSC fetch path, not the dev server.Expected behavior
After
router.pushwith new search params, the server component should receive the updatedsearchParamsand re-render with the correct filtered data (matching Next.js behavior).Environment
vinext dev(works) andwrangler dev(broken)Root cause
In Next.js, accessing
searchParamsopts a page into dynamic rendering. The page should never be ISR-cached, it should render fresh on every request. vinext has the same intent withmarkDynamicUsage(), which fires insidebuildPageElement()when the page has search params. The HTML response path checks this correctly viaconsumeDynamicUsage()and skips the ISR cache write. But the RSC response path (used for client-side navigation) returns before that check ever runs, so it writes to the ISR cache unconditionally.In
entries/app-rsc-entry.ts:markDynamicUsage()fires at line ~1188 duringbuildPageElement()when search params are presentconsumeDynamicUsage()at line ~3170 and skips ISR if true. This works.dynamicUsedDuringRenderguard.So what happens is:
/top?genre=Romance&provider=1899hits the RSC path, renders correctly, then writes the result to ISR cache under key/top(pathname only, which is the correct key format for ISR)/top?genre=Romance&provider=8hits the ISR cache, gets a HIT on/top, and returns stale content from request 1The ISR cache key being pathname-only is fine and matches Next.js. The bug is just that the RSC path doesn't respect the dynamic rendering signal before caching.
Fix
Move the
consumeDynamicUsage()check before the RSC return path, or add it as a guard on the RSC cache write block:Same guard should probably be added to the RSC stream tee setup at line ~2950 to avoid the unnecessary tee when we know we won't cache.
@james-elicx commented on GitHub (Mar 19, 2026):
This is intentional and aligns with the behaviour in Next.js - you'll probably want to rewrite requests with query params to dynamic routes if they should form part of your ISR cache keys.
This one sound problematic and like an issue that needs addressing - if a route uses dynamic APIs, that should be accounted for before attempting to cache the response.
Similarly to the first one, this is also intentional due to search params not forming part of the ISR cache.
@NathanDrake2406 commented on GitHub (Mar 20, 2026):
I've updated the issue :)