You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a public, non-breaking X-React-Router-Request-Intent request header to data requests so loaders, actions, and middleware can distinguish navigation requests (the user is moving to a new route) from fetch requests (the user stays put — revalidations, useFetcher loads/submits, and form submissions to the current route).
Splitting this out from #14505 per the suggestion to scope a separate RFC for request-type detection.
Motivation: auth-redirect target selection
In our app every loader/middleware may hit a redirect-through-the-auth-provider flow. After the round-trip the app must decide where to land the user, and the correct destination depends on the intent of the original request:
Navigation → the user asked to be at request.url. Redirect there.
Fetch / revalidation → the user is already at Referer. Redirect there to preserve their visual location.
Today there is no reliable way to tell these apart on the server. request.url and the _routes query param are not enough — useRevalidator(), useFetcher().load(...), and a <Link> navigation can all produce the same-shaped URL.
We currently handle this with a client-side patch that adds the header (link in the Reference Patch section). This RFC proposes adopting it into the framework.
Scenarios
User is on /dashboard unless noted. Each scenario assumes the request hits an expired session and triggers the auth round-trip.
#
Trigger
Request
Intent
Redirect after auth
1
Initial document load of /dashboard
GET /dashboard
navigation (no header needed)
/dashboard
2
<Link to="/settings"> clicked
GET /settings.data?_routes=…
navigation
/settings
3
useRevalidator()
GET /dashboard.data?_routes=…
fetch
Referer (/dashboard)
4
useFetcher().load("/users")
GET /users.data?_routes=…
fetch
Referer (/dashboard) — user does not land on /users
Cases 3 and 4 are the ones that are impossible to disambiguate from case 2 today.
Proposed API
A request header set client-side by the router on data requests:
X-React-Router-Request-Intent: navigation | fetch
navigation — originated from a navigation (<Link>, navigate(), <Form> to a different route, etc).
fetch — originated from anything that does not change the user's location: useRevalidator(), useFetcher().load/submit, <Form> submitted to the current route.
Document requests do not need the header — they can always be treated as navigation requests. The header exists only on .data requests, where the ambiguity is. The proposal in #14505 helps us distinguish between document vs .data requests.
Read in loaders/actions/middleware via request.headers.get("X-React-Router-Request-Intent").
Reference patch
cerbos/react-router @ 8702c05. The patch uses the header name X-Request-Type; this RFC proposes X-React-Router-Request-Intent to align with existing framework-owned headers.
Behaviour:
createClientSideRequest adds fetch when the header is missing.
startNavigation accepts opts.isRevalidation; when falsy it adds navigation. When truthy it leaves the header alone, so the request falls through to createClientSideRequest and is tagged fetch.
revalidate always passes isRevalidation: true to startNavigation.
Net result: navigations get navigation, everything else (revalidations, fetcher loads/submits, current-route form submissions) gets fetch. Manual testing confirmed correct values for all of those. Happy to bring this to PR quality with full test coverage if there's interest.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Summary
Add a public, non-breaking
X-React-Router-Request-Intentrequest header to data requests so loaders, actions, and middleware can distinguish navigation requests (the user is moving to a new route) from fetch requests (the user stays put — revalidations,useFetcherloads/submits, and form submissions to the current route).Splitting this out from #14505 per the suggestion to scope a separate RFC for request-type detection.
Motivation: auth-redirect target selection
In our app every loader/middleware may hit a redirect-through-the-auth-provider flow. After the round-trip the app must decide where to land the user, and the correct destination depends on the intent of the original request:
request.url. Redirect there.Referer. Redirect there to preserve their visual location.Today there is no reliable way to tell these apart on the server.
request.urland the_routesquery param are not enough —useRevalidator(),useFetcher().load(...), and a<Link>navigation can all produce the same-shaped URL.We currently handle this with a client-side patch that adds the header (link in the Reference Patch section). This RFC proposes adopting it into the framework.
Scenarios
User is on
/dashboardunless noted. Each scenario assumes the request hits an expired session and triggers the auth round-trip./dashboardGET /dashboard/dashboard<Link to="/settings">clickedGET /settings.data?_routes=…/settingsuseRevalidator()GET /dashboard.data?_routes=…Referer(/dashboard)useFetcher().load("/users")GET /users.data?_routes=…Referer(/dashboard) — user does not land on/users<Form method="post">to current routePOST /dashboard.data?_routes=…Referer; preserve/replay form stateuseFetcher().submit(…)POST /target.data?_routes=…Referer; preserve/replay form state/parent/a→/parent/b)GET /parent/b.data?_routes=root,parent,b/parent/b(single target for the batch)Cases 3 and 4 are the ones that are impossible to disambiguate from case 2 today.
Proposed API
A request header set client-side by the router on data requests:
navigation— originated from a navigation (<Link>,navigate(),<Form>to a different route, etc).fetch— originated from anything that does not change the user's location:useRevalidator(),useFetcher().load/submit,<Form>submitted to the current route.Document requests do not need the header — they can always be treated as navigation requests. The header exists only on
.datarequests, where the ambiguity is. The proposal in #14505 helps us distinguish betweendocumentvs.datarequests.Read in loaders/actions/middleware via
request.headers.get("X-React-Router-Request-Intent").Reference patch
cerbos/react-router @ 8702c05. The patch uses the header name
X-Request-Type; this RFC proposesX-React-Router-Request-Intentto align with existing framework-owned headers.Behaviour:
createClientSideRequestaddsfetchwhen the header is missing.startNavigationacceptsopts.isRevalidation; when falsy it addsnavigation. When truthy it leaves the header alone, so the request falls through tocreateClientSideRequestand is taggedfetch.revalidatealways passesisRevalidation: truetostartNavigation.Net result: navigations get
navigation, everything else (revalidations, fetcher loads/submits, current-route form submissions) getsfetch. Manual testing confirmed correct values for all of those. Happy to bring this to PR quality with full test coverage if there's interest.All reactions