>('https://jsonplaceholder.typicode.com/posts')
+ .then((r) => r.data.slice(0, 10))
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/a.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/a.tsx
new file mode 100644
index 0000000000..6cccd02950
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/a.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/_first/_second/layout-a')({
+ component: LayoutAComponent,
+})
+
+function LayoutAComponent() {
+ return I'm layout A!
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/b.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/b.tsx
new file mode 100644
index 0000000000..98bb842612
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/b.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/_first/_second/layout-b')({
+ component: LayoutBComponent,
+})
+
+function LayoutBComponent() {
+ return I'm layout B!
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/index.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/index.tsx
new file mode 100644
index 0000000000..c7417e5eeb
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/index.tsx
@@ -0,0 +1,5 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/classic/hello/')({
+ component: () => This is the index
,
+})
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/route.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/route.tsx
new file mode 100644
index 0000000000..566efc8777
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/route.tsx
@@ -0,0 +1,27 @@
+import { Link, Outlet, createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/classic/hello')({
+ component: () => (
+
+ Hello!
+
{' '}
+
+ say hello to the universe
+ {' '}
+
+ say hello to the world
+
+
+
+ ),
+})
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/universe.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/universe.tsx
new file mode 100644
index 0000000000..e00c47d74b
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/universe.tsx
@@ -0,0 +1,5 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/classic/hello/universe')({
+ component: () => Hello /classic/hello/universe!
,
+})
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/world.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/world.tsx
new file mode 100644
index 0000000000..9783557342
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/file-based-subtree/hello/world.tsx
@@ -0,0 +1,5 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/classic/hello/world')({
+ component: () => Hello /classic/hello/world!
,
+})
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/home.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/home.tsx
new file mode 100644
index 0000000000..eac82a9174
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/home.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react'
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/')({
+ component: Home,
+})
+
+function Home() {
+ return (
+
+
Welcome Home!
+
+ )
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/first-layout.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/first-layout.tsx
new file mode 100644
index 0000000000..d39e206f2d
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/first-layout.tsx
@@ -0,0 +1,16 @@
+import { Outlet, createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/_first')({
+ component: LayoutComponent,
+})
+
+function LayoutComponent() {
+ return (
+
+ )
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/second-layout.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/second-layout.tsx
new file mode 100644
index 0000000000..ef178a6e16
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/layout/second-layout.tsx
@@ -0,0 +1,34 @@
+import { Link, Outlet, createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/_first/_second')({
+ component: LayoutComponent,
+})
+
+function LayoutComponent() {
+ return (
+
+
I'm a nested layout
+
+
+ Layout A
+
+
+ Layout B
+
+
+
+
+
+
+ )
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-detail.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-detail.tsx
new file mode 100644
index 0000000000..948d52d6d6
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-detail.tsx
@@ -0,0 +1,28 @@
+import * as React from 'react'
+import { ErrorComponent, createFileRoute } from '@tanstack/react-router'
+import { fetchPost } from '../../posts'
+import type { ErrorComponentProps } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/posts/$postId')({
+ loader: async ({ params: { postId } }) => fetchPost(postId),
+ errorComponent: PostErrorComponent as any,
+ notFoundComponent: () => {
+ return Post not found
+ },
+ component: PostComponent,
+})
+
+export function PostErrorComponent({ error }: ErrorComponentProps) {
+ return
+}
+
+function PostComponent() {
+ const post = Route.useLoaderData()
+
+ return (
+
+
{post.title}
+
{post.body}
+
+ )
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-home.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-home.tsx
new file mode 100644
index 0000000000..056433ca0a
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts-home.tsx
@@ -0,0 +1,10 @@
+import * as React from 'react'
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/posts/')({
+ component: PostsIndexComponent,
+})
+
+function PostsIndexComponent() {
+ return Select a post.
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts.tsx
new file mode 100644
index 0000000000..a2ab1ee388
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/posts/posts.tsx
@@ -0,0 +1,39 @@
+import * as React from 'react'
+import { Link, Outlet, createFileRoute } from '@tanstack/react-router'
+import { fetchPosts } from '../../posts'
+
+export const Route = createFileRoute('/posts')({
+ loader: fetchPosts,
+ component: PostsComponent,
+})
+
+function PostsComponent() {
+ const posts = Route.useLoaderData()
+
+ return (
+
+ )
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/root.tsx b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/root.tsx
new file mode 100644
index 0000000000..05ac9527f3
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/src/routes/root.tsx
@@ -0,0 +1,70 @@
+import * as React from 'react'
+import { Link, Outlet, createRootRoute } from '@tanstack/react-router'
+import { TanStackRouterDevtools } from '@tanstack/router-devtools'
+
+export const Route = createRootRoute({
+ component: RootComponent,
+ notFoundComponent: () => {
+ return (
+
+
This is the notFoundComponent configured on root route
+
Start Over
+
+ )
+ },
+})
+
+function RootComponent() {
+ return (
+ <>
+
+
+ Home
+ {' '}
+
+ Posts
+ {' '}
+
+ Layout
+ {' '}
+
+ Subtree
+ {' '}
+
+ This Route Does Not Exist
+
+
+
+
+ {/* Start rendering router matches */}
+
+ >
+ )
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tests/app.spec.ts b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tests/app.spec.ts
new file mode 100644
index 0000000000..10beff655f
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tests/app.spec.ts
@@ -0,0 +1,33 @@
+import { expect, test } from '@playwright/test'
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('/')
+})
+
+test('Navigating to a post page', async ({ page }) => {
+ await page.getByRole('link', { name: 'Posts' }).click()
+ await page.getByRole('link', { name: 'sunt aut facere repe' }).click()
+ await expect(page.getByRole('heading')).toContainText('sunt aut facere')
+})
+
+test('Navigating nested layouts', async ({ page }) => {
+ await page.getByRole('link', { name: 'Layout', exact: true }).click()
+
+ await expect(page.locator('#root')).toContainText("I'm a layout")
+ await expect(page.locator('#root')).toContainText("I'm a nested layout")
+
+ await page.getByRole('link', { name: 'Layout A' }).click()
+ await expect(page.locator('#root')).toContainText("I'm layout A!")
+
+ await page.getByRole('link', { name: 'Layout B' }).click()
+ await expect(page.locator('#root')).toContainText("I'm layout B!")
+})
+
+test('Navigating to a not-found route', async ({ page }) => {
+ await page.getByRole('link', { name: 'This Route Does Not Exist' }).click()
+ await expect(page.getByRole('paragraph')).toContainText(
+ 'This is the notFoundComponent configured on root route',
+ )
+ await page.getByRole('link', { name: 'Start Over' }).click()
+ await expect(page.getByRole('heading')).toContainText('Welcome Home!')
+})
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tsconfig.json b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tsconfig.json
new file mode 100644
index 0000000000..7dc6d51cf9
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "moduleResolution": "Bundler",
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
+ "module": "ESNext",
+ "jsx": "react-jsx",
+ "strict": true,
+ "skipLibCheck": true,
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "useDefineForClassFields": true,
+ "allowJs": true
+ },
+ "include": ["src", "playwright.config.ts", "tests", "./routes.ts"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tsr.config.json b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tsr.config.json
new file mode 100644
index 0000000000..2759341ba2
--- /dev/null
+++ b/e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tsr.config.json
@@ -0,0 +1,5 @@
+{
+ "routesDirectory": "./src/routes",
+ "generatedRouteTree": "./src/routeTree.gen.ts",
+ "virtualRouteConfig": "./routes.ts"
+}
diff --git a/e2e/utils.js b/e2e/utils.js
index d0090dde51..a1626c67f2 100644
--- a/e2e/utils.js
+++ b/e2e/utils.js
@@ -16,5 +16,8 @@ export function derivePort(input, min = 5600, max = 65535) {
// Map hash value to the port range
const port = min + (hashInt % (max - min + 1))
+
+ console.info(`Mapped "${input}" to port ${port}`)
+
return port
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0196c5a305..b2b0582227 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -475,6 +475,89 @@ importers:
specifier: ^6.0.3
version: 6.0.3(@types/node@22.10.1)(jiti@2.4.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.6.1)
+ e2e/react-router/rspack-basic-file-based:
+ dependencies:
+ '@tanstack/react-router':
+ specifier: workspace:*
+ version: link:../../../packages/react-router
+ '@tanstack/router-devtools':
+ specifier: workspace:*
+ version: link:../../../packages/router-devtools
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ redaxios:
+ specifier: ^0.5.1
+ version: 0.5.1
+ devDependencies:
+ '@playwright/test':
+ specifier: ^1.49.0
+ version: 1.49.0
+ '@rsbuild/core':
+ specifier: 1.1.9
+ version: 1.1.9
+ '@rsbuild/plugin-react':
+ specifier: 1.1.0
+ version: 1.1.0(@rsbuild/core@1.1.9)
+ '@tanstack/router-plugin':
+ specifier: workspace:*
+ version: link:../../../packages/router-plugin
+ '@types/react':
+ specifier: ^18.3.3
+ version: 18.3.12
+ '@types/react-dom':
+ specifier: ^18.3.0
+ version: 18.3.1
+ typescript:
+ specifier: ^5.6.2
+ version: 5.7.2
+
+ e2e/react-router/rspack-basic-virtual-named-export-config-file-based:
+ dependencies:
+ '@tanstack/react-router':
+ specifier: workspace:*
+ version: link:../../../packages/react-router
+ '@tanstack/router-devtools':
+ specifier: workspace:*
+ version: link:../../../packages/router-devtools
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ redaxios:
+ specifier: ^0.5.1
+ version: 0.5.1
+ devDependencies:
+ '@playwright/test':
+ specifier: ^1.49.0
+ version: 1.49.0
+ '@rsbuild/core':
+ specifier: 1.1.9
+ version: 1.1.9
+ '@rsbuild/plugin-react':
+ specifier: 1.1.0
+ version: 1.1.0(@rsbuild/core@1.1.9)
+ '@tanstack/router-plugin':
+ specifier: workspace:*
+ version: link:../../../packages/router-plugin
+ '@tanstack/virtual-file-routes':
+ specifier: workspace:*
+ version: link:../../../packages/virtual-file-routes
+ '@types/react':
+ specifier: ^18.3.3
+ version: 18.3.12
+ '@types/react-dom':
+ specifier: ^18.3.0
+ version: 18.3.1
+ typescript:
+ specifier: ^5.6.2
+ version: 5.7.2
+
e2e/react-router/scroll-restoration-sandbox-vite:
dependencies:
'@tanstack/react-router':