diff --git a/packages/react-router/src/Transitioner.tsx b/packages/react-router/src/Transitioner.tsx
index 04e140acfc..882778b3c6 100644
--- a/packages/react-router/src/Transitioner.tsx
+++ b/packages/react-router/src/Transitioner.tsx
@@ -52,10 +52,10 @@ export function Transitioner() {
_includeValidateSearch: true,
})
- if (
- trimPathRight(router.latestLocation.href) !==
- trimPathRight(nextLocation.href)
- ) {
+ const latestPublicHref = trimPathRight(router.latestLocation.publicHref)
+ const nextPublicHref = trimPathRight(nextLocation.publicHref)
+
+ if (latestPublicHref !== nextPublicHref) {
router.commitLocation({ ...nextLocation, replace: true })
}
diff --git a/packages/react-router/tests/router.test.tsx b/packages/react-router/tests/router.test.tsx
index 2214e9001e..a2834301f8 100644
--- a/packages/react-router/tests/router.test.tsx
+++ b/packages/react-router/tests/router.test.tsx
@@ -2836,6 +2836,72 @@ describe('rewriteBasepath utility', () => {
expect(router.state.location.pathname).toBe('/test')
})
+ it('should handle basepath when accessing root path and maintain basepath in browser URL', async () => {
+ const rootRoute = createRootRoute({
+ component: () => ,
+ })
+
+ const indexRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: '/',
+ component: () =>
Home
,
+ })
+
+ const routeTree = rootRoute.addChildren([indexRoute])
+
+ const history = createMemoryHistory({
+ initialEntries: ['/my-app/'],
+ })
+
+ const router = createRouter({
+ routeTree,
+ history,
+ rewrite: rewriteBasepath({ basepath: '/my-app' }),
+ })
+
+ render()
+
+ await waitFor(() => {
+ expect(screen.getByTestId('home')).toBeInTheDocument()
+ })
+
+ expect(router.state.location.pathname).toBe('/')
+ expect(history.location.pathname).toBe('/my-app/')
+ })
+
+ it('should handle basepath option for backward compatibility', async () => {
+ const rootRoute = createRootRoute({
+ component: () => ,
+ })
+
+ const indexRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: '/',
+ component: () => Home
,
+ })
+
+ const routeTree = rootRoute.addChildren([indexRoute])
+
+ const history = createMemoryHistory({
+ initialEntries: ['/my-app/'],
+ })
+
+ const router = createRouter({
+ routeTree,
+ history,
+ basepath: '/my-app',
+ })
+
+ render()
+
+ await waitFor(() => {
+ expect(screen.getByTestId('home')).toBeInTheDocument()
+ })
+
+ expect(router.state.location.pathname).toBe('/')
+ expect(history.location.pathname).toBe('/my-app/')
+ })
+
it('should combine basepath with additional input rewrite logic', async () => {
const rootRoute = createRootRoute({
component: () => ,
diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts
index 8c644db78b..fc0d71c374 100644
--- a/packages/router-core/src/router.ts
+++ b/packages/router-core/src/router.ts
@@ -290,12 +290,15 @@ export interface RouterOptions<
* ```ts
* const router = createRouter({
* routeTree,
- * rewrite: rewriteBasepath('/basepath')
- * // Or wrap existing rewrite functionality
- * rewrite: rewriteBasepath('/basepath', {
- * output: ({ url }) => {...},
- * input: ({ url }) => {...},
- * })
+ * rewrite: rewriteBasepath({ basepath: '/basepath' })
+ * // Or compose with existing rewrite functionality
+ * rewrite: composeRewrites([
+ * rewriteBasepath({ basepath: '/basepath', caseSensitive: true }),
+ * {
+ * input: ({ url }) => {...},
+ * output: ({ url }) => {...},
+ * }
+ * ])
* })
* ```
* @default '/'
@@ -1688,7 +1691,7 @@ export class RouterCore<
return {
publicHref:
rewrittenUrl.pathname + rewrittenUrl.search + rewrittenUrl.hash,
- href: fullPath,
+ href: rewrittenUrl.href.replace(rewrittenUrl.origin, ''),
url: rewrittenUrl.href,
pathname: nextPathname,
search: nextSearch,
@@ -1784,8 +1787,12 @@ export class RouterCore<
return isEqual
}
+ const latestPublicHref =
+ this.latestLocation.publicHref ?? this.latestLocation.href
+ const nextPublicHref = next.publicHref ?? next.href
+
const isSameUrl =
- trimPathRight(this.latestLocation.href) === trimPathRight(next.href)
+ trimPathRight(latestPublicHref) === trimPathRight(nextPublicHref)
const previousCommitPromise = this.commitLocationPromise
this.commitLocationPromise = createControlledPromise(() => {
@@ -1942,11 +1949,14 @@ export class RouterCore<
}
}
+ const latestPublicHref = this.latestLocation.publicHref
+ const nextPublicHref = nextLocation.publicHref
+
if (
- trimPath(normalizeUrl(this.latestLocation.href)) !==
- trimPath(normalizeUrl(nextLocation.href))
+ trimPath(normalizeUrl(latestPublicHref)) !==
+ trimPath(normalizeUrl(nextPublicHref))
) {
- throw redirect({ href: nextLocation.href })
+ throw redirect({ href: nextPublicHref })
}
}
diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx
index 2d602a2022..6aa52ba6dd 100644
--- a/packages/solid-router/src/Transitioner.tsx
+++ b/packages/solid-router/src/Transitioner.tsx
@@ -50,10 +50,10 @@ export function Transitioner() {
_includeValidateSearch: true,
})
- if (
- trimPathRight(router.latestLocation.href) !==
- trimPathRight(nextLocation.href)
- ) {
+ const latestPublicHref = trimPathRight(router.latestLocation.publicHref)
+ const nextPublicHref = trimPathRight(nextLocation.publicHref)
+
+ if (latestPublicHref !== nextPublicHref) {
router.commitLocation({ ...nextLocation, replace: true })
}