Basepaths like "base%2Fpath" work properly in version 1.43.4 but not in 1.45.11
Describe the bug
I'm trying to create a router on a basepath containing the value %2F (URL encoded representation of /).
With @tanstack/react-router version 1.43.4 (edit: and 1.45.0), the router respects it, and renders links to /base%2Fpath/any-page correctly. Example: https://stackblitz.com/edit/tanstack-router-kaytm9?file=src%2Fmain.tsx
With version 1.45.11, the router, after root route mount, redirects the browser to /base/path and links are rendered incorrectly to /base/path/any-page.
Example: https://stackblitz.com/edit/tanstack-router-dvwena?file=src%2Fmain.tsx
Your Example Website or App
https://stackblitz.com/edit/tanstack-router-dvwena?file=src%2Fmain.tsx
Steps to Reproduce the Bug or Issue
- Create router with basepath containing
%2F, like/base%2Fpathand serve the page with the router at<domain>/base%2Fpath. - With Chrome (or with probably any other browser), open
<domain>/base%2Fpath. - Router mounts, root route mounts, and changes the URL in the address bar to
<domain>/base/path. Links are rendered pointing to locations with the changed basepath, for example<domain>/base/path/any-page.
Expected behavior
I expect browser location <domain>/base%2Fpath not to change. I expect Links to point to <domain>/base%2Fpath/any-page.
Screenshots or Videos
No response
Platform
- OS: macOS 14
- Browser: Chrome
- Version: 126.0.6478.183 (Official Build) (arm64)
Additional context
As far as I can tell after debugging, the location is changed after rootRoute onEnter() hook. At that point, the basepath ends up being decoded here https://github.com/TanStack/router/blame/main/packages/react-router/src/path.ts#L184. There's no workarounds that I could find, even moving from basepath to splat.
Tried some more versions. Does not reproduce in 1.45.0: https://stackblitz.com/edit/tanstack-router-dmarzj?file=src%2Fmain.tsx
After that version, issue is present in all versions to 1.45.11.
out of curiosity, why do you need this kind of basepath?
out of curiosity, why do you need this kind of basepath?
Thanks for engaging!
We have a service that customers can install for <customer-domain> and/or <customer-other-domain>/<subpath>. From the standpoint of our service, these installs are equal and valid, and are accessed in one level deep path at <our-service-domain>/<customer-domain> and <our-service-domain>/<customer-other-domain>%2F<subpath> respectively.
Imagine https://www.whois.com/whois/example.xyz, https://www.whois.com/whois/example.xyz%2Fpage
I wasn't able to get this working refactoring to splat as well, so I think there's a deeper issue at play here.
A naive fix occurred to me
In packages/react-router/src/path.ts parsePathname function
const decodeURIComponentExceptForwardSlash = (
pathSegment: string,
): string => {
return pathSegment.split(/%2F/i).map(decodeURIComponent).join('%2F')
}
return {
type: 'pathname',
value: decodeURIComponentExceptForwardSlash(part),
}
In packages/react-router/tests/router.test.tsx an additional case
describe('encoding: URL path segment', () => {
it.each([
{
input: '/path-segment/%C3%A9',
output: '/path-segment/é',
type: 'is encoded',
},
{
input: '/path-segment/é',
output: '/path-segment/é',
type: 'is not encoded',
},
{
input: '/path-segment/%F0%9F%9A%80',
output: '/path-segment/🚀',
type: 'is encoded',
},
{
input: '/path-segment/🚀',
output: '/path-segment/🚀',
type: 'is not encoded',
},
{
input: '/%F0%9F%9A%80toMoon%2f🚀toMars%2F🚀toVenus/page',
output: '/🚀toMoon%2F🚀toMars%2F🚀toVenus/page',
type: 'contains some encoded characters and an encoded forward slash',
},
])(
'should resolve $input to $output when the path segment $type',
async ({ input, output }) => {
const { router } = createTestRouter(
createMemoryHistory({ initialEntries: [input] }),
)
render(<RouterProvider router={router} />)
await act(() => router.load())
expect(router.state.location.pathname).toBe(output)
},
)
})
@apata can you see if #2261 fixes your problem?
Retested with the preview package https://pkg.pr.new/@tanstack/react-router@2261 in https://stackblitz.com/edit/tanstack-router-xevuta?file=package.json and can confirm that this issue is fixed.