Path params starting with `$` are rewritten to "undefined" on page load
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.1k
- Forks
- 1.9k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 143
Description
Which project does this relate to?
Router
Describe the bug
A path param value starting with $ is destroyed on full page load: the router rewrites the URL to /files/undefined via history.replaceState and the param value becomes the string "undefined".
With pathParamsAllowedCharacters: ['$'], the router itself generates such URLs: a <Link to="/files/$filePath" params={{ filePath: '$EXAMPLE_CODE/file.abap' }}> navigates to /files/$EXAMPLE_CODE%2Ffile.abap and works fine client-side, but reloading that URL mangles it.
Claude's root cause analysis: on mount, <Transitioner> round-trips the current location through buildLocation({ to: router.latestLocation.pathname, ... }). buildLocation treats to as a route template, so interpolatePath interprets the concrete segment $EXAMPLE_CODE%2Ffile.abap as a param placeholder named EXAMPLE_CODE%2Ffile.abap.
No such param exists, so it interpolates "undefined" (encodeParam(key, params, decoder) ?? "undefined" in router-core's path.ts). The rebuilt publicHref differs from the current one, so the broken location is committed with replace: true. A second symptom of the same ambiguity: the rebuilt to containing $ doesn't exactly match any route template, so buildLocation sets destRoutes = [] and skips all search middlewares for that navigation.
Complete minimal reproducer
https://stackblitz.com/edit/github-arh22bkl?file=src%2Fmain.tsx
Steps to Reproduce the Bug
- npm install && npm run dev
- Open http://localhost:5173/ and click the link — the URL becomes /files/$EXAMPLE_CODE%2Ffile.abap and the view renders the param correctly
- Reload the page
- The URL is replaced with /files/undefined and useParams() returns filePath: "undefined"
Expected behavior
As a user, I expected a page reload of a router-generated URL to render the same route with the same param value, but the URL is rewritten to /files/undefined and the param value is lost.
Screenshots or Videos
No response
Platform
- Router / Start Version: 1.169.2
- OS: macOS 26.5.1
- Browser: Chrome
- Browser Version: 149
- Bundler: vite
- Bundler Version: 8.0.10 (reproduction uses vite 7 as I ran into some napi crashes in Stackblitz)
Additional context
Workaround: percent-encode $ as %24 in the pathname (e.g. via history.replaceState) before the router parses the location.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in router-core's path.ts, especially interpolatePath and the encodeParam fallback, then trace how Transitioner calls buildLocation on mount. Use the linked StackBlitz reproducer to verify the reload case, and consider regression coverage that confirms a router-generated path containing a leading $ keeps its URL and parameter value after reload.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 67/100