diff --git a/packages/react-router/src/Transitioner.tsx b/packages/react-router/src/Transitioner.tsx
index f547cdd5f58..9cbf5995166 100644
--- a/packages/react-router/src/Transitioner.tsx
+++ b/packages/react-router/src/Transitioner.tsx
@@ -52,11 +52,16 @@ export function Transitioner() {
_includeValidateSearch: true,
})
- if (
- trimPathRight(router.latestLocation.href) !==
- trimPathRight(nextLocation.href)
- ) {
- router.commitLocation({ ...nextLocation, replace: true })
+ const latestPublicHref = trimPathRight(router.latestLocation.publicHref)
+ const nextPublicHref = trimPathRight(nextLocation.publicHref)
+
+ if (latestPublicHref !== nextPublicHref) {
+ router.navigate({
+ to: nextLocation.pathname,
+ search: nextLocation.search,
+ hash: nextLocation.hash,
+ replace: true,
+ })
}
return () => {
diff --git a/packages/react-router/tests/router.test.tsx b/packages/react-router/tests/router.test.tsx
index 32fc3efa071..38db9e5eb4c 100644
--- a/packages/react-router/tests/router.test.tsx
+++ b/packages/react-router/tests/router.test.tsx
@@ -3126,6 +3126,72 @@ describe('basepath', () => {
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,
+ 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 19b77531297..ce379e62f8f 100644
--- a/packages/router-core/src/router.ts
+++ b/packages/router-core/src/router.ts
@@ -1866,8 +1866,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(() => {
@@ -1997,10 +2001,26 @@ export class RouterCore<
} catch {}
}
+ const nextLocation = this.buildLocation({
+ to,
+ ...rest,
+ _includeValidateSearch: true,
+ _isNavigate: true,
+ } as any)
+
+ if (!reloadDocument) {
+ const currentOrigin = new URL(this.latestLocation.url).origin
+ const nextOrigin = new URL(nextLocation.url).origin
+
+ if (currentOrigin !== nextOrigin) {
+ reloadDocument = true
+ href = nextLocation.url
+ }
+ }
+
if (reloadDocument) {
if (!href) {
- const location = this.buildLocation({ to, ...rest } as any)
- href = location.url
+ href = nextLocation.url
}
// Check blockers for external URLs unless ignoreBlocker is true
@@ -2030,11 +2050,13 @@ export class RouterCore<
return Promise.resolve()
}
- return this.buildAndCommitLocation({
- ...rest,
- href,
- to: to as string,
- _isNavigate: true,
+ return this.commitLocation({
+ ...nextLocation,
+ replace: rest.replace,
+ resetScroll: rest.resetScroll,
+ hashScrollIntoView: rest.hashScrollIntoView,
+ viewTransition: rest.viewTransition,
+ ignoreBlocker: rest.ignoreBlocker,
})
}
diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx
index 1708f1bcb8c..ab8f25ecd56 100644
--- a/packages/solid-router/src/Transitioner.tsx
+++ b/packages/solid-router/src/Transitioner.tsx
@@ -55,10 +55,20 @@ 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) {
+ const latestOrigin = new URL(router.latestLocation.url).origin
+ const nextOrigin = new URL(nextLocation.url).origin
+
+ if (latestOrigin !== nextOrigin) {
+ if (typeof window !== 'undefined') {
+ window.location.href = nextLocation.url
+ }
+ return
+ }
+
router.commitLocation({ ...nextLocation, replace: true })
}