Replies: 1 comment
|
Converted to a discussion according to the Open Governance model |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
React Router framework mode lacks a hook to transform asset and single-fetch URLs at SSR time. This blocks platforms (Vercel, Netlify, etc.) from implementing version-skew protection the way Next.js, SvelteKit, Astro, Qwik, and Nuxt do — by suffixing a deployment id (e.g.
?dpl=<id>) onto every framework-managed client request so stale HTML still resolves its content-hashed chunks after a new deploy.Related downstream issue: vercel/vercel#16604 —
@vercel/react-routercan't implement automatic skew protection without a framework-level hook.Background
When a CDN-cached HTML response references content-hashed assets (
/assets/chunk-abc123.js), and a new deploy ships, returning users load the stale HTML and request chunks that no longer exist in the new deployment. The result is a wave of 404s — each hitting the SSR function on Vercel — every deploy.Supported frameworks solve this by injecting
?dpl=<VERCEL_DEPLOYMENT_ID>(read from env at SSR) into:<script>/<link>URLs in the rendered HTML.dataloader requestsVercel's edge reads the param and routes the request to the matching deployment. See Vercel — Skew Protection.
The Vercel docs list React Router as not automatically supported. The documented workaround is setting a
__vdplcookie inentry.server.tsx, butSet-Cookiemakes the HTML response uncacheable on Vercel's CDN — forcing apps to choose between CDN cache and skew protection. The query-param mechanism used by other frameworks avoids the tradeoff.Proposal
Add a public API for transforming framework-managed URLs at SSR time. Two reasonable shapes:
Option A —
entry.serverexportCalled once per URL when
<Scripts>/<Links>/useFetcher/ single-fetch builds a framework-managed URL. Covers:manifest.urlmanifest.entry.moduleandimportsmodule,imports,css,client*Module,hydrateFallbackModule${pathname}.dataURL constructed for single-fetch loader requestsPrefetchPageLinkshrefsThe result must be serialized into
__remixManifest/__remixContextso client-side navigation also uses transformed URLs.Option B — config in
react-router.config.tsA is more flexible (per-request access if signature accepts
request). B is more declarative.Why a framework hook is the right layer
@react-router/devand consumed by<Scripts>/<Links>/ single-fetch — none of which today expose a transform seam.UNSAFE_*) shared manifest object inentry.server(fragile across RR versions) or stream-rewrite the rendered HTML (chunk-boundary hazards, can't reach__remixManifestconsistently, and doesn't cover.datarequests).@vercel/react-router) can't implement this cleanly without a framework hook either — they'd have to fork or patch RR internals. See vercel/vercel#16604.Once the hook exists, the Vercel preset (and equivalents for other hosts) becomes a one-liner that reads
VERCEL_DEPLOYMENT_IDand wires the transform.Scope of "framework-managed" URLs
.dataURLsPrefetchPageLinkshrefs<a href>/<Link to>— these are app concerns, not frameworkAlternatives considered
__vdpl) — what the Vercel docs recommend today; kills CDN cache on every SSR'd response.EntryContext.manifestinentry.server— works but usesUNSAFE_AssetsManifesttypes and a shared object reference; breaks silently on schema changes.__remixManifest, doesn't cover.data.A first-class hook is the only durable answer.
Prior art
process.env.NEXT_DEPLOYMENT_IDand appends?dpl=framework-wide.config.kit.version.pollInterval+ automatic asset URL pinning whenversion.nameis set.Happy to draft an RFC / PR if there's interest in either shape.
🤖 Filed by Claude on behalf of @ap-justin
All reactions