diff --git a/e2e/react-router/basepath-file-based/.gitignore b/e2e/react-router/basepath-file-based/.gitignore
new file mode 100644
index 00000000000..4d2da67b504
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/.gitignore
@@ -0,0 +1,11 @@
+node_modules
+.DS_Store
+dist
+dist-hash
+dist-ssr
+*.local
+
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/e2e/react-router/basepath-file-based/index.html b/e2e/react-router/basepath-file-based/index.html
new file mode 100644
index 00000000000..21e30f16951
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/react-router/basepath-file-based/package.json b/e2e/react-router/basepath-file-based/package.json
new file mode 100644
index 00000000000..bfe2ec82f2f
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "tanstack-router-e2e-react-basepath-file-based",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port 3000",
+ "dev:e2e": "vite",
+ "build": "vite build && tsc --noEmit",
+ "serve": "vite preview",
+ "start": "vite",
+ "test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
+ },
+ "dependencies": {
+ "@tanstack/react-router": "workspace:^",
+ "@tanstack/react-router-devtools": "workspace:^",
+ "@tanstack/router-plugin": "workspace:^",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.50.1",
+ "@tanstack/router-e2e-utils": "workspace:^",
+ "@types/react": "^19.0.8",
+ "@types/react-dom": "^19.0.3",
+ "@vitejs/plugin-react": "^4.3.4",
+ "vite": "^7.1.7"
+ }
+}
diff --git a/e2e/react-router/basepath-file-based/playwright.config.ts b/e2e/react-router/basepath-file-based/playwright.config.ts
new file mode 100644
index 00000000000..4dc2271f01e
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/playwright.config.ts
@@ -0,0 +1,41 @@
+import { defineConfig, devices } from '@playwright/test'
+import {
+ getDummyServerPort,
+ getTestServerPort,
+} from '@tanstack/router-e2e-utils'
+import packageJson from './package.json' with { type: 'json' }
+
+const PORT = await getTestServerPort(packageJson.name)
+const EXTERNAL_PORT = await getDummyServerPort(packageJson.name)
+const baseURL = `http://localhost:${PORT}`
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ workers: 1,
+
+ reporter: [['line']],
+
+ globalSetup: './tests/setup/global.setup.ts',
+ globalTeardown: './tests/setup/global.teardown.ts',
+
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ baseURL,
+ },
+
+ webServer: {
+ command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm serve --port ${PORT}`,
+ url: baseURL,
+ reuseExistingServer: !process.env.CI,
+ stdout: 'pipe',
+ },
+
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ ],
+})
diff --git a/e2e/react-router/basepath-file-based/src/main.tsx b/e2e/react-router/basepath-file-based/src/main.tsx
new file mode 100644
index 00000000000..28f985de688
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/src/main.tsx
@@ -0,0 +1,27 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { RouterProvider, createRouter } from '@tanstack/react-router'
+import { routeTree } from './routeTree.gen'
+
+// Set up a Router instance
+const router = createRouter({
+ routeTree,
+ defaultPreload: 'intent',
+ defaultStaleTime: 5000,
+ scrollRestoration: true,
+ basepath: '/app',
+})
+
+// Register things for typesafety
+declare module '@tanstack/react-router' {
+ interface Register {
+ router: typeof router
+ }
+}
+
+const rootElement = document.getElementById('app')!
+
+if (!rootElement.innerHTML) {
+ const root = ReactDOM.createRoot(rootElement)
+ root.render()
+}
diff --git a/e2e/react-router/basepath-file-based/src/routeTree.gen.ts b/e2e/react-router/basepath-file-based/src/routeTree.gen.ts
new file mode 100644
index 00000000000..59499d9fbf3
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/src/routeTree.gen.ts
@@ -0,0 +1,77 @@
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file was automatically generated by TanStack Router.
+// You should NOT make any changes in this file as it will be overwritten.
+// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
+
+import { Route as rootRouteImport } from './routes/__root'
+import { Route as AboutRouteImport } from './routes/about'
+import { Route as IndexRouteImport } from './routes/index'
+
+const AboutRoute = AboutRouteImport.update({
+ id: '/about',
+ path: '/about',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const IndexRoute = IndexRouteImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRouteImport,
+} as any)
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '/about': typeof AboutRoute
+}
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '/about': typeof AboutRoute
+}
+export interface FileRoutesById {
+ __root__: typeof rootRouteImport
+ '/': typeof IndexRoute
+ '/about': typeof AboutRoute
+}
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/' | '/about'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/' | '/about'
+ id: '__root__' | '/' | '/about'
+ fileRoutesById: FileRoutesById
+}
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ AboutRoute: typeof AboutRoute
+}
+
+declare module '@tanstack/react-router' {
+ interface FileRoutesByPath {
+ '/about': {
+ id: '/about'
+ path: '/about'
+ fullPath: '/about'
+ preLoaderRoute: typeof AboutRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ }
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ AboutRoute: AboutRoute,
+}
+export const routeTree = rootRouteImport
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
diff --git a/e2e/react-router/basepath-file-based/src/routes/__root.tsx b/e2e/react-router/basepath-file-based/src/routes/__root.tsx
new file mode 100644
index 00000000000..9c657c7d5b4
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/src/routes/__root.tsx
@@ -0,0 +1,3 @@
+import { createRootRoute } from '@tanstack/react-router'
+
+export const Route = createRootRoute()
diff --git a/e2e/react-router/basepath-file-based/src/routes/about.tsx b/e2e/react-router/basepath-file-based/src/routes/about.tsx
new file mode 100644
index 00000000000..2d0dba2d6fb
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/src/routes/about.tsx
@@ -0,0 +1,20 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/about')({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ const navigate = Route.useNavigate()
+
+ return (
+
+
+
+ )
+}
diff --git a/e2e/react-router/basepath-file-based/src/routes/index.tsx b/e2e/react-router/basepath-file-based/src/routes/index.tsx
new file mode 100644
index 00000000000..9975e63698d
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/src/routes/index.tsx
@@ -0,0 +1,25 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/')({
+ component: App,
+})
+
+function App() {
+ const navigate = Route.useNavigate()
+
+ return (
+
+
+
+ )
+}
diff --git a/e2e/react-router/basepath-file-based/tests/reload-document.test.ts b/e2e/react-router/basepath-file-based/tests/reload-document.test.ts
new file mode 100644
index 00000000000..3e60f3bedcb
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/tests/reload-document.test.ts
@@ -0,0 +1,18 @@
+import { expect, test } from '@playwright/test'
+
+test('navigate() respects basepath for when reloadDocument=true', async ({
+ page,
+}) => {
+ await page.goto(`/app/`)
+ await expect(page.getByTestId(`home-component`)).toBeInViewport()
+
+ const aboutBtn = page.getByTestId(`to-about-btn`)
+ await aboutBtn.click()
+ await page.waitForURL('/app/about')
+ await expect(page.getByTestId(`about-component`)).toBeInViewport()
+
+ const homeBtn = page.getByTestId(`to-home-btn`)
+ await homeBtn.click()
+ await page.waitForURL('/app/')
+ await expect(page.getByTestId(`home-component`)).toBeInViewport()
+})
diff --git a/e2e/react-router/basepath-file-based/tests/setup/global.setup.ts b/e2e/react-router/basepath-file-based/tests/setup/global.setup.ts
new file mode 100644
index 00000000000..3593d10ab90
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/tests/setup/global.setup.ts
@@ -0,0 +1,6 @@
+import { e2eStartDummyServer } from '@tanstack/router-e2e-utils'
+import packageJson from '../../package.json' with { type: 'json' }
+
+export default async function setup() {
+ await e2eStartDummyServer(packageJson.name)
+}
diff --git a/e2e/react-router/basepath-file-based/tests/setup/global.teardown.ts b/e2e/react-router/basepath-file-based/tests/setup/global.teardown.ts
new file mode 100644
index 00000000000..62fd79911cc
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/tests/setup/global.teardown.ts
@@ -0,0 +1,6 @@
+import { e2eStopDummyServer } from '@tanstack/router-e2e-utils'
+import packageJson from '../../package.json' with { type: 'json' }
+
+export default async function teardown() {
+ await e2eStopDummyServer(packageJson.name)
+}
diff --git a/e2e/react-router/basepath-file-based/tsconfig.json b/e2e/react-router/basepath-file-based/tsconfig.json
new file mode 100644
index 00000000000..82cf0bcd2c9
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "esModuleInterop": true,
+ "jsx": "react-jsx",
+ "target": "ESNext",
+ "moduleResolution": "Bundler",
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "resolveJsonModule": true,
+ "allowJs": true,
+ "types": ["vite/client"]
+ },
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/e2e/react-router/basepath-file-based/vite.config.js b/e2e/react-router/basepath-file-based/vite.config.js
new file mode 100644
index 00000000000..784341f8bc4
--- /dev/null
+++ b/e2e/react-router/basepath-file-based/vite.config.js
@@ -0,0 +1,9 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import { tanstackRouter } from '@tanstack/router-plugin/vite'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ base: '/app/',
+ plugins: [tanstackRouter({ target: 'react' }), react()],
+})
diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts
index 46c52c9ec38..78b5e11608b 100644
--- a/packages/router-core/src/router.ts
+++ b/packages/router-core/src/router.ts
@@ -1916,7 +1916,7 @@ export class RouterCore<
if (reloadDocument) {
if (!href) {
const location = this.buildLocation({ to, ...rest } as any)
- href = location.href
+ href = location.url
}
if (rest.replace) {
window.location.replace(href)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d040a1b385c..a3dd14049b3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -162,6 +162,43 @@ importers:
specifier: ^4.5.4
version: 4.5.4(@types/node@22.10.2)(rollup@4.52.2)(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0))
+ e2e/react-router/basepath-file-based:
+ dependencies:
+ '@tanstack/react-router':
+ specifier: workspace:*
+ version: link:../../../packages/react-router
+ '@tanstack/react-router-devtools':
+ specifier: workspace:^
+ version: link:../../../packages/react-router-devtools
+ '@tanstack/router-plugin':
+ specifier: workspace:*
+ version: link:../../../packages/router-plugin
+ react:
+ specifier: ^19.0.0
+ version: 19.0.0
+ react-dom:
+ specifier: ^19.0.0
+ version: 19.0.0(react@19.0.0)
+ devDependencies:
+ '@playwright/test':
+ specifier: ^1.52.0
+ version: 1.52.0
+ '@tanstack/router-e2e-utils':
+ specifier: workspace:^
+ version: link:../../e2e-utils
+ '@types/react':
+ specifier: ^19.0.8
+ version: 19.0.8
+ '@types/react-dom':
+ specifier: ^19.0.3
+ version: 19.0.3(@types/react@19.0.8)
+ '@vitejs/plugin-react':
+ specifier: ^4.3.4
+ version: 4.7.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0))
+ vite:
+ specifier: ^7.1.7
+ version: 7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)
+
e2e/react-router/basic:
dependencies:
'@tanstack/react-router':
@@ -25645,28 +25682,28 @@ snapshots:
pluralize@8.0.0: {}
- postcss-import@15.1.0(postcss@8.5.3):
+ postcss-import@15.1.0(postcss@8.5.6):
dependencies:
- postcss: 8.5.3
+ postcss: 8.5.6
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.10
- postcss-js@4.0.1(postcss@8.5.3):
+ postcss-js@4.0.1(postcss@8.5.6):
dependencies:
camelcase-css: 2.0.1
- postcss: 8.5.3
+ postcss: 8.5.6
- postcss-load-config@4.0.2(postcss@8.5.3):
+ postcss-load-config@4.0.2(postcss@8.5.6):
dependencies:
lilconfig: 3.1.3
yaml: 2.7.0
optionalDependencies:
- postcss: 8.5.3
+ postcss: 8.5.6
- postcss-nested@6.2.0(postcss@8.5.3):
+ postcss-nested@6.2.0(postcss@8.5.6):
dependencies:
- postcss: 8.5.3
+ postcss: 8.5.6
postcss-selector-parser: 6.1.2
postcss-selector-parser@6.1.2:
@@ -26581,11 +26618,11 @@ snapshots:
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.1.1
- postcss: 8.5.3
- postcss-import: 15.1.0(postcss@8.5.3)
- postcss-js: 4.0.1(postcss@8.5.3)
- postcss-load-config: 4.0.2(postcss@8.5.3)
- postcss-nested: 6.2.0(postcss@8.5.3)
+ postcss: 8.5.6
+ postcss-import: 15.1.0(postcss@8.5.6)
+ postcss-js: 4.0.1(postcss@8.5.6)
+ postcss-load-config: 4.0.2(postcss@8.5.6)
+ postcss-nested: 6.2.0(postcss@8.5.6)
postcss-selector-parser: 6.1.2
resolve: 1.22.10
sucrase: 3.35.0