-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Add support for <Link unstable_rewrite> #14716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: 3d54c92 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| "window.history.replaceState({ ...window.history.state, rewrite: undefined }, null);", | ||
| "}", | ||
| ].join("") | ||
| : ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we cannot support history.state-driven rewrites during SSR, we just clear out the rewrite location on SSR renders so the client just works with the normal browser URL location to avoid hydration issues.
| push(to, state) { | ||
| action = Action.Push; | ||
| let nextLocation = createMemoryLocation(to, state); | ||
| let nextLocation = isLocation(to) ? to : createMemoryLocation(to, state); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accept Locations in our history.push/replace API so that we can just proxy the rewrite along in the location
| ...initialLocation, | ||
| ...initialLocation.rewrite, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hydrate the rewrite location if it exists
| ? // `matchRoutes()` has already been called if we're in here via `router.initialize()` | ||
| state.matches | ||
| : matchRoutes(routesToUse, location, basename); | ||
| : matchRoutes(routesToUse, routerPath, basename); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Matching/data loading operate against the routerPath, which is the rewrite if it exists
| !(opts && opts.submission && isMutationMethod(opts.submission.formMethod)) | ||
| ) { | ||
| completeNavigation(location, { matches }, { flushSync }); | ||
| completeNavigation(externalLocation, { matches }, { flushSync }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But when we complete the navigation, we do it to the external/user-facing to location
| search: state.location.rewrite.search, | ||
| hash: state.location.rewrite.hash, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: I think this should probably move deeper now that rewrite lives on location and isn't just a magic field in location.state
| @@ -1,9 +1,25 @@ | |||
| import { useLoaderData, useLocation } from "react-router"; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
playground/framework is updated with an example usage - can toggle between ssr:true/ssr:false to see the differences in behavior on hard reloads
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Revert these playground changes before merging
|
@brophdawg11 Just tested it for the Unsplash asset page modal and it seems to work perfectly. 😘 |
|
An interesting edge case I just encountered:
const location = useLocation();
const [searchParams] = useSearchParams();
searchParams.set('foo', 'bar');
return <Link to={{ pathname: location.pathname, search: searchParams.toString() }}>
Add query param
</Link>Result: The old modal setup (using location state) didn't have this problem. The URL in this case would be TanStack has declarative route masking. I wonder if this would help: https://tanstack.com/router/v1/docs/framework/react/guide/route-masking##declarative-route-masking For context, where this shows up in Unsplash is our (nested) modals: Screen.Recording.2026-01-09.at.16.13.58.mov |
|
Something else I noticed is that the location exposed by https://stackblitz.com/edit/github-p8mgs2ph?file=src%2Fapp.tsx
|
|
nvm got my wires crossed. That's because of this quick hack: #14716 (comment) Just to clarify - do you want |
I expected both the hook and |
|
Side note: I wonder if it's easier to think about this feature in terms of "route masking" (like TanStack Router) rather than "rewriting". It flips them around: Rewriting: <Link to="/photos/abc" rewrite="/?photo=abc" />Route masking: <Link to="/?photo=abc" mask="/photos/abc" /> |

RFC: #9864
Add support for
<Link unstable_rewrite>which allows users to navigate to one URL in the browser but "rewrite" the url that is processed by the router, permitting contextual routing usages such as displaying an image in a model on top of a gallery.This brings the long-standing example of doing this manually in declarative mode into Data/Framework Mode for client side navigations.