Repository: TanStack/router
Pull request #2666 (2024-10-28 21:06:02)

fix(react-router): `parentMatchPromise`resolves to parent match

nx-cloud[bot]: # ☁️ Nx Cloud Report

CI is running for commit dc61450da83464e9131b5c6d861d736c474d834d.

[📂 Click to track the progress, see the status, the terminal output, and the build insights.](https://cloud.nx.app/cipes/671ffcd098bb1b5759a13b78?utm_source=pull-request&utm_medium=comment)


---

Sent with 💌 from [NxCloud.](https://nx.app?utm_source=pull-request&utm_medium=comment)
pkg-pr-new[bot]: 
[Open in Stackblitz](https://pkg.pr.new/template/59beb7c5-279e-4d5f-bd0e-0afeb48bc69f)
<details><summary><b>More templates</b></summary><p>

- [tanstack-router-react-example-authenticated-routes](https://pkg.pr.new/template/fe069a98-9ea0-4baf-b647-789c0b4badfd)
- [tanstack-router-react-example-basic](https://pkg.pr.new/template/0effa9b8-0c8a-40e4-82d2-20075773ae0c)
- [tanstack-router-react-example-basic-default-search-params](https://pkg.pr.new/template/c4101bd3-a08a-4f3a-9e44-6aa3bca5cc1e)
- [tanstack-router-react-example-basic-file-based-codesplitting](https://pkg.pr.new/template/18d1ef63-e956-4304-8de2-4441910a9cb3)
- [tanstack-router-react-example-basic-file-based](https://pkg.pr.new/template/aefe1e1b-b147-47f1-afe8-b6b7290d52c3)
- [tanstack-router-react-example-react-query](https://pkg.pr.new/template/29825b9d-2afa-450d-97ed-0ffa7cadd96c)
- [tanstack-router-react-example-basic-react-query-file-based](https://pkg.pr.new/template/779dc04b-c36b-4d85-9f68-0429ecc67683)
- [tanstack-router-react-example-basic-ssr-file-based](https://pkg.pr.new/template/26c4cd6d-6085-4a8c-9f46-aa7548d0a25f)
- [tanstack-router-react-example-basic-ssr-streaming-file-based](https://pkg.pr.new/template/05a8a3c9-e23e-42af-a6ba-01be7f5ef7ae)
- [tanstack-router-react-example-basic-virtual-file-based](https://pkg.pr.new/template/0d065024-1128-4a38-960a-23f1897dfb92)
- [tanstack-router-react-example-basic-virtual-inside-file-based](https://pkg.pr.new/template/a0fd8ec9-497a-4247-a265-0bffc478c12a)
- [tanstack-router-react-example-deferred-data](https://pkg.pr.new/template/b6d9f6e3-8888-4fef-bba5-0a841cb4b568)
- [tanstack-router-react-example-kitchen-sink-file-based](https://pkg.pr.new/template/c59a5081-0b82-435b-8eef-99c35f54e007)
- [tanstack-router-react-example-kitchen-sink](https://pkg.pr.new/template/0416c455-26a5-431e-b07f-d91e25e10dcb)
- [tanstack-router-react-example-kitchen-sink-react-query-file-based](https://pkg.pr.new/template/d1ff1b60-5a5b-4571-8ffd-3c36d9af1fe6)
- [tanstack-router-react-example-kitchen-sink-react-query](https://pkg.pr.new/template/be305212-425e-4941-a08a-0b389b65b596)
- [tanstack-router-react-example-large-file-based](https://pkg.pr.new/template/ec128c6d-78fa-42f3-83e1-46d42c9a5a8b)
- [tanstack-router-react-example-location-masking](https://pkg.pr.new/template/f31a5383-4ec8-4441-8696-e5b781da6099)
- [tanstack-router-react-example-navigation-blocking](https://pkg.pr.new/template/2c2c261b-6e9c-4a1c-a42c-03e38e28d63b)
- [tanstack-router-react-example-quickstart](https://pkg.pr.new/template/ee83cb19-daa0-48f2-ab4a-4b064075d6b9)
- [tanstack-router-react-example-quickstart-esbuild-file-based](https://pkg.pr.new/template/1d61bb48-5b47-42c4-87d3-a954e58382e1)
- [tanstack-router-react-example-quickstart-file-based](https://pkg.pr.new/template/cadf8da8-6f1f-4f7f-b6b4-4db9c8a8734d)
- [tanstack-router-react-example-quickstart-rspack-file-based](https://pkg.pr.new/template/1db13660-456c-42c9-a8b3-8876fee4a987)
- [tanstack-router-react-example-quickstart-webpack-file-based](https://pkg.pr.new/template/2fb3bc76-62d6-402b-86e9-4df2a912ee8f)
- [router-monorepo-react-query](https://pkg.pr.new/template/b190a983-8cf2-4a27-a26f-00fcd98d77a3)
- [router-mono-simple](https://pkg.pr.new/template/b4b31aa0-aade-4652-a9a3-fec6bb10f008)
- [tanstack-router-react-example-scroll-restoration](https://pkg.pr.new/template/cad7acc4-31ac-48f5-8381-88ad08b874dd)
- [tanstack-router-search-validator-adapters](https://pkg.pr.new/template/c3f04ca4-3037-48a9-926a-622be5b5915f)
- [tanstack-start-example-basic](https://pkg.pr.new/template/d6768430-9673-457f-9ff3-afcd0488f845)
- [tanstack-start-example-basic-auth](https://pkg.pr.new/template/05d2a0b8-ecdc-4ddc-8dbb-2915863fa497)
- [tanstack-start-example-basic-react-query](https://pkg.pr.new/template/81e6d158-301b-4bbf-be13-2db4a9219008)
- [tanstack-start-example-basic-rsc](https://pkg.pr.new/template/77c1d15a-f509-4b1b-ad37-4ce65925912d)
- [tanstack-start-example-clerk-basic](https://pkg.pr.new/template/cec1bc01-76e8-47f2-9f43-eba3b2769535)
- [tanstack-start-example-convex-trellaux](https://pkg.pr.new/template/ead4dc93-c037-4911-9aa8-841b247e59c0)
- [tanstack-start-example-counter](https://pkg.pr.new/template/e0304828-0e58-42f1-9e3c-4fafc50fb9a0)
- [tanstack-start-example-supabase-basic](https://pkg.pr.new/template/78ba6854-976d-4025-a9c9-1ad591b6a4dd)
- [tanstack-start-example-trellaux](https://pkg.pr.new/template/504796c5-bc5d-4f8c-a821-0dafd13075cb)
- [tanstack-router-react-example-with-framer-motion](https://pkg.pr.new/template/2f6332d3-0198-4090-9843-6693d050e8fa)
- [tanstack-router-react-example-with-trpc](https://pkg.pr.new/template/ce076dd6-5f0a-4779-a425-6eec61fb333e)
- [tanstack-router-react-example-with-trpc-react-query](https://pkg.pr.new/template/620ba6b4-6fa1-4ae5-aeda-5cdc686bf308)

</p></details>
      
    


<details><summary><b>@tanstack/create-router</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/create-router@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/eslint-plugin-router</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/eslint-plugin-router@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/react-cross-context</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/react-cross-context@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/history</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/history@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/react-router</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/react-router@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/react-router-with-query</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/react-router-with-query@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/router-arktype-adapter</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/router-arktype-adapter@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/router-cli</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/router-cli@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/router-devtools</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/router-devtools@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/router-generator</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/router-generator@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/router-plugin</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/router-plugin@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/router-valibot-adapter</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/router-valibot-adapter@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/router-vite-plugin</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/router-vite-plugin@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/router-zod-adapter</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/router-zod-adapter@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/start</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/start@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/start-vite-plugin</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/start-vite-plugin@2666
```

</p></details>
      
    

<details><summary><b>@tanstack/virtual-file-routes</b></summary><p>

```
pnpm add https://pkg.pr.new/@tanstack/virtual-file-routes@2666
```

</p></details>
      
    

_commit: <a href="https://github.com/TanStack/router/runs/32184339846"><code>dc61450</code></a>_


Patch:
diff --git a/docs/framework/react/guide/data-loading.md b/docs/framework/react/guide/data-loading.md
index 24748a7e9..b966418a2 100644
--- a/docs/framework/react/guide/data-loading.md
+++ b/docs/framework/react/guide/data-loading.md
@@ -78,7 +78,7 @@ The `loader` function receives a single object with the following properties:
 - `deps` - The object value returned from the `Route.loaderDeps` function. If `Route.loaderDeps` is not defined, an empty object will be provided instead.
 - `location` - The current location
 - `params` - The route's path params
-- `parentMatchPromise` - `Promise<void>` or `undefined`
+- `parentMatchPromise` - `Promise<RouteMatch>` or `undefined`
 - `preload` - Boolean which is `true` when the route is being preloaded instead of loaded
 - `route` - The route itself
 
diff --git a/packages/react-router/src/Matches.tsx b/packages/react-router/src/Matches.tsx
index 36dc9ae25..a99403938 100644
--- a/packages/react-router/src/Matches.tsx
+++ b/packages/react-router/src/Matches.tsx
@@ -121,6 +121,16 @@ export const isMatch = <TMatch, TPath extends string>(
   return value != null
 }
 
+export type MakeRouteMatchFromRoute<TRoute extends AnyRoute> = RouteMatch<
+  TRoute['types']['id'],
+  TRoute['types']['fullPath'],
+  TRoute['types']['allParams'],
+  TRoute['types']['fullSearchSchema'],
+  TRoute['types']['loaderData'],
+  TRoute['types']['allContext'],
+  TRoute['types']['loaderDeps']
+>
+
 export interface RouteMatch<
   TRouteId,
   TFullPath,
diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts
index 1bc5c8890..3a6aef13c 100644
--- a/packages/react-router/src/route.ts
+++ b/packages/react-router/src/route.ts
@@ -11,7 +11,12 @@ import { rootRouteId } from './root'
 import type * as React from 'react'
 import type { RootRouteId } from './root'
 import type { UseNavigateResult } from './useNavigate'
-import type { MakeRouteMatch, MakeRouteMatchUnion, RouteMatch } from './Matches'
+import type {
+  MakeRouteMatch,
+  MakeRouteMatchFromRoute,
+  MakeRouteMatchUnion,
+  RouteMatch,
+} from './Matches'
 import type { NavigateOptions, ParsePathParams, ToMaskOptions } from './link'
 import type { ParsedLocation } from './location'
 import type { RouteById, RouteIds, RoutePaths } from './routeInfo'
@@ -591,7 +596,7 @@ export interface LoaderFnContext<
    * @deprecated Use `throw redirect({ to: '/somewhere' })` instead
    **/
   navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-  parentMatchPromise?: Promise<void>
+  parentMatchPromise?: Promise<MakeRouteMatchFromRoute<TParentRoute>>
   cause: 'preload' | 'enter' | 'stay'
   route: Route
 }
diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts
index a1030c8d5..da1bf6890 100644
--- a/packages/react-router/src/router.ts
+++ b/packages/react-router/src/router.ts
@@ -2299,7 +2299,7 @@ export class Router<
             }
 
             const validResolvedMatches = matches.slice(0, firstBadMatchIndex)
-            const matchPromises: Array<Promise<any>> = []
+            const matchPromises: Array<Promise<AnyRouteMatch>> = []
 
             validResolvedMatches.forEach(({ id: matchId, routeId }, index) => {
               matchPromises.push(
@@ -2535,6 +2535,7 @@ export class Router<
                     loaderPromise: undefined,
                     invalid: false,
                   }))
+                  return this.getMatch(matchId)!
                 })(),
               )
             })


Test patch:
diff --git a/packages/react-router/tests/loaders.test.tsx b/packages/react-router/tests/loaders.test.tsx
index 5f25c8a89..896ffa106 100644
--- a/packages/react-router/tests/loaders.test.tsx
+++ b/packages/react-router/tests/loaders.test.tsx
@@ -134,14 +134,17 @@ describe('loaders parentMatchPromise', () => {
       path: '/nested',
       loader: async () => {
         await sleep(WAIT_TIME)
+        return 'nested'
       },
       component: () => <Outlet />,
     })
     const fooRoute = createRoute({
       getParentRoute: () => nestedRoute,
       path: '/foo',
-      loader: ({ parentMatchPromise }) => {
+      loader: async ({ parentMatchPromise }) => {
         nestedLoaderMock(parentMatchPromise)
+        const parentMatch = await parentMatchPromise
+        expect(parentMatch?.loaderData).toBe('nested')
       },
       component: () => <div>Nested Foo page</div>,
     })
diff --git a/packages/react-router/tests/route.test-d.tsx b/packages/react-router/tests/route.test-d.tsx
index 69286c8c0..65bdb69b0 100644
--- a/packages/react-router/tests/route.test-d.tsx
+++ b/packages/react-router/tests/route.test-d.tsx
@@ -16,7 +16,11 @@ import type {
   Route,
   SearchSchemaInput,
 } from '../src'
-import type { MakeRouteMatchUnion } from '../src/Matches'
+import type {
+  MakeRouteMatch,
+  MakeRouteMatchFromRoute,
+  MakeRouteMatchUnion,
+} from '../src/Matches'
 
 test('when creating the root', () => {
   const rootRoute = createRootRoute()
@@ -83,7 +87,7 @@ test('when creating the root with a loader', () => {
         context: {}
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatch>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>()
@@ -178,7 +182,7 @@ test('when creating the root route with context and a loader', () => {
         context: { userId: string }
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatch>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>()
@@ -246,7 +250,7 @@ test('when creating the root route with context, routeContext, beforeLoad and a
         context: { userId: string; permission: 'view'; env: 'env1' }
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatch>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>()
@@ -374,7 +378,7 @@ test('when creating a child route with a loader from the root route', () => {
         context: {}
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatchFromRoute<typeof rootRoute>>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>()
@@ -416,7 +420,7 @@ test('when creating a child route with a loader from the root route with context
         context: { userId: string }
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatchFromRoute<typeof rootRoute>>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>()
@@ -549,7 +553,7 @@ test('when creating a child route with params, search and loader from the root r
         context: {}
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatchFromRoute<typeof rootRoute>>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>
@@ -574,7 +578,7 @@ test('when creating a child route with params, search, loader and loaderDeps fro
         context: {}
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatchFromRoute<typeof rootRoute>>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>(),
@@ -598,7 +602,7 @@ test('when creating a child route with params, search, loader and loaderDeps fro
         context: { userId: string }
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatchFromRoute<typeof rootRoute>>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>(),
@@ -701,7 +705,7 @@ test('when creating a child route with params, search with routeContext, beforeL
         context: { userId: string; env: string; readonly permission: 'view' }
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<MakeRouteMatchFromRoute<typeof rootRoute>>
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>()
@@ -1030,7 +1034,9 @@ test('when creating a child route with routeContext, beforeLoad, search, params,
         }
         location: ParsedLocation
         navigate: (opts: NavigateOptions<AnyRouter>) => Promise<void>
-        parentMatchPromise?: Promise<void>
+        parentMatchPromise?: Promise<
+          MakeRouteMatchFromRoute<typeof detailsRoute>
+        >
         cause: 'preload' | 'enter' | 'stay'
         route: Route
       }>(),