Repository: TanStack/router (2025-07-11T22:08:42Z)

Issue:
Weird routing behaviour with nested dynamic parameters and shared `route.tsx`
### Which project does this relate to?

Router

### Describe the bug

Having the following tree:

```
❯ tree
.
├── __root.tsx
├── index.tsx
└── project
    ├── $projectId
    │   └── (manage)
    │       ├── $taskId
    │       │   ├── $assigneeId.tsx
    │       │   └── index.tsx
    │       └── route.tsx
    └── route.tsx
```

And navigating to the `/project/123/456` route produces unexpected routing and weird values for dynamic parameters.


### Your Example Website or App

https://github.com/TmLev/tanstack-router-routing-bug

### Steps to Reproduce the Bug or Issue

1. Navigate to the `/project/123/456` route
2. Inspect the router devtools


### Expected behavior

I was expecting to land on the `/project/$projectId/(manage)/$taskId/index.tsx` route with `$projectId=123` and `$taskId=456`.

In reality, I ended up on the `/project/$projectId/(manage)/$taskId/$assigneeId.tsx` route with `$projectId=undefined` and `$taskId=123` and `$assigneeId=456`.

### Screenshots or Videos

![Image](https://github.com/user-attachments/assets/b38b21b5-b391-4019-975c-39f81672a2ff)

### Platform

- Router / Start Version: ^1.125.4
- OS: macOS 14.7.2
- Browser: Arc (Chromium-based)
- Browser Version: Version 1.99.0 (64212) Chromium Engine Version 137.0.7151.104
- Bundler: Vite
- Bundler Version: ^6.1.0


### Additional context

_No response_


Discussion:
Hi! This is most likely a bug in the codegen package, as some of the routes have wrong ids and paths. I'll fix it and write some tests.

```ts
const ProjectProjectIdmanageTaskIdIndexRoute =
  ProjectProjectIdmanageTaskIdIndexRouteImport.update({
    id: '/$taskId/',
    path: '/$taskId/',
    getParentRoute: () => ProjectProjectIdmanageRouteRoute,
  } as any)
const ProjectProjectIdmanageTaskIdAssigneeIdRoute =
  ProjectProjectIdmanageTaskIdAssigneeIdRouteImport.update({
    id: '/$taskId/$assigneeId',
    path: '/$taskId/$assigneeId',
    getParentRoute: () => ProjectProjectIdmanageRouteRoute,
  } as any)
```
My bad, it's not a bug in the codegen package.

For the record, I added a console log here https://github.com/TanStack/router/blob/d728d699005aa2c4d891d1a332ee516f259deabb/packages/router-core/src/route.ts#L1511

and this is what I get:

<img width="919" height="129" alt="Image" src="https://github.com/user-attachments/assets/8debac09-e24e-4cc0-86a9-1d130c11b58f" />

so it looks like the route with id `/project/$projectId/(manage)` lacks a `path`, which means that after`/project/$projectId/(manage)/$taskId/$assigneeId` concatenates its parent's fullPath (`/project`), and its own path (`$taskId/$assigneeId`) to calculate its own path `/project/$taskId/$assigneeId`), with `$projectId` missing.

---

Adding the following if here https://github.com/TanStack/router/blob/d728d699005aa2c4d891d1a332ee516f259deabb/packages/router-core/src/route.ts#L1507

```
    if (path === undefined) {
      path = "/$projectId"
    }

```

fixes the problem, though obviously it's a hacky workaround. I'll look for the root cause.

# Update

The codegen doesn't emit the path param

```
const ProjectProjectIdmanageRouteRoute =
  ProjectProjectIdmanageRouteRouteImport.update({
    id: '/$projectId/(manage)',
    // path prop is missing, other routes have it
    getParentRoute: () => ProjectRouteRoute,
  } as any)
```

which is decided here

https://github.com/TanStack/router/blob/ef76302df40c914a716bcf03df0bbb693893cbfe/packages/router-generator/src/generator.ts#L598

And whether a route is a path route or not is decided here https://github.com/TanStack/router/blob/ef76302df40c914a716bcf03df0bbb693893cbfe/packages/router-generator/src/generator.ts#L1263



Patch:
diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts
index 8246c4ef103..6d868af1ab2 100644
--- a/packages/router-generator/src/generator.ts
+++ b/packages/router-generator/src/generator.ts
@@ -1260,7 +1260,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
 
     node.isNonPath =
       lastRouteSegment.startsWith('_') ||
-      this.routeGroupPatternRegex.test(lastRouteSegment)
+      split.every((part) => this.routeGroupPatternRegex.test(part))
 
     node.cleanedPath = removeGroups(
       removeUnderscores(removeLayoutSegments(node.path)) ?? '',


Test patch:
diff --git a/packages/router-generator/tests/generator/path-above-route-in-group/routeTree.snapshot.ts b/packages/router-generator/tests/generator/path-above-route-in-group/routeTree.snapshot.ts
new file mode 100644
index 00000000000..1c216c5bcd4
--- /dev/null
+++ b/packages/router-generator/tests/generator/path-above-route-in-group/routeTree.snapshot.ts
@@ -0,0 +1,87 @@
+/* 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 ABcRouteRouteImport } from './routes/a/$b/(c)/route'
+import { Route as ABcDEIndexRouteImport } from './routes/a/$b/(c)/d/e/index'
+
+const ABcRouteRoute = ABcRouteRouteImport.update({
+  id: '/a/$b/(c)',
+  path: '/a/$b/',
+  getParentRoute: () => rootRouteImport,
+} as any)
+const ABcDEIndexRoute = ABcDEIndexRouteImport.update({
+  id: '/d/e/',
+  path: '/d/e/',
+  getParentRoute: () => ABcRouteRoute,
+} as any)
+
+export interface FileRoutesByFullPath {
+  '/a/$b': typeof ABcRouteRouteWithChildren
+  '/a/$b/d/e': typeof ABcDEIndexRoute
+}
+export interface FileRoutesByTo {
+  '/a/$b': typeof ABcRouteRouteWithChildren
+  '/a/$b/d/e': typeof ABcDEIndexRoute
+}
+export interface FileRoutesById {
+  __root__: typeof rootRouteImport
+  '/a/$b/(c)': typeof ABcRouteRouteWithChildren
+  '/a/$b/(c)/d/e/': typeof ABcDEIndexRoute
+}
+export interface FileRouteTypes {
+  fileRoutesByFullPath: FileRoutesByFullPath
+  fullPaths: '/a/$b' | '/a/$b/d/e'
+  fileRoutesByTo: FileRoutesByTo
+  to: '/a/$b' | '/a/$b/d/e'
+  id: '__root__' | '/a/$b/(c)' | '/a/$b/(c)/d/e/'
+  fileRoutesById: FileRoutesById
+}
+export interface RootRouteChildren {
+  ABcRouteRoute: typeof ABcRouteRouteWithChildren
+}
+
+declare module '@tanstack/react-router' {
+  interface FileRoutesByPath {
+    '/a/$b/(c)': {
+      id: '/a/$b/(c)'
+      path: '/a/$b'
+      fullPath: '/a/$b'
+      preLoaderRoute: typeof ABcRouteRouteImport
+      parentRoute: typeof rootRouteImport
+    }
+    '/a/$b/(c)/d/e/': {
+      id: '/a/$b/(c)/d/e/'
+      path: '/d/e'
+      fullPath: '/a/$b/d/e'
+      preLoaderRoute: typeof ABcDEIndexRouteImport
+      parentRoute: typeof ABcRouteRoute
+    }
+  }
+}
+
+interface ABcRouteRouteChildren {
+  ABcDEIndexRoute: typeof ABcDEIndexRoute
+}
+
+const ABcRouteRouteChildren: ABcRouteRouteChildren = {
+  ABcDEIndexRoute: ABcDEIndexRoute,
+}
+
+const ABcRouteRouteWithChildren = ABcRouteRoute._addFileChildren(
+  ABcRouteRouteChildren,
+)
+
+const rootRouteChildren: RootRouteChildren = {
+  ABcRouteRoute: ABcRouteRouteWithChildren,
+}
+export const routeTree = rootRouteImport
+  ._addFileChildren(rootRouteChildren)
+  ._addFileTypes<FileRouteTypes>()
diff --git a/packages/router-generator/tests/generator/path-above-route-in-group/routes/__root.tsx b/packages/router-generator/tests/generator/path-above-route-in-group/routes/__root.tsx
new file mode 100644
index 00000000000..f89644e82d4
--- /dev/null
+++ b/packages/router-generator/tests/generator/path-above-route-in-group/routes/__root.tsx
@@ -0,0 +1,11 @@
+import * as React from 'react'
+import { Outlet, createRootRoute } from '@tanstack/react-router'
+
+export const Route = createRootRoute({
+  component: () => (
+    <React.Fragment>
+      <div>Hello "__root"!</div>
+      <Outlet />
+    </React.Fragment>
+  ),
+})
diff --git a/packages/router-generator/tests/generator/path-above-route-in-group/routes/a/$b/(c)/d/e/index.tsx b/packages/router-generator/tests/generator/path-above-route-in-group/routes/a/$b/(c)/d/e/index.tsx
new file mode 100644
index 00000000000..a16a0e49b06
--- /dev/null
+++ b/packages/router-generator/tests/generator/path-above-route-in-group/routes/a/$b/(c)/d/e/index.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/a/$b/(c)/d/e/')({
+  component: RouteComponent,
+})
+
+function RouteComponent() {
+  return <div>Hello "/a/$b/(c)/d/e/"!</div>
+}
diff --git a/packages/router-generator/tests/generator/path-above-route-in-group/routes/a/$b/(c)/route.tsx b/packages/router-generator/tests/generator/path-above-route-in-group/routes/a/$b/(c)/route.tsx
new file mode 100644
index 00000000000..a93b75dd2e4
--- /dev/null
+++ b/packages/router-generator/tests/generator/path-above-route-in-group/routes/a/$b/(c)/route.tsx
@@ -0,0 +1,4 @@
+import { createFileRoute } from '@tanstack/react-router'
+export const Route = createFileRoute('/a/$b/(c)')({
+  component: () => <div>Hello /a/$b/(c)!</div>,
+})