Repository: TanStack/router
Pull request #1713 (2024-06-05 21:28:55)

fix(react-router): route-context being available and loaders being called after a redirection
This PR initially was just to add tests for the route-context checks after navigation, but it became a bit more.

- It checks route context being available after a navigation.
- It checks route context being available after a redirection.
- It checks that the destination route loaders are being fired when redirected to.
- The tests introduced in #1710 are also being amended to better simulate a "real-world experience". No more relying on calls to `router.navigate()`, instead a button is clicked which calls `navigate` from `useNavigate()`.
- The tests in `packages/react-router/tests/redirects.test.tsx` related to route-context have been __REMOVED__ in favor of the ones in `packages/react-router/tests/routeContext.test.tsx` as they simulate real-world usage.

I've manually tested these changes against the reproductions in the following issues and can confirm that this PR fixes the following.

- #1730
- #1670 
- #1650 
- This [comment](https://github.com/TanStack/router/issues/1656#issuecomment-2145989592) in #1656

Context-related issues still remain broken in this though.

- The original reproduction in #1656

Thanks to @freshgiammi [pointing out](https://github.com/TanStack/router/pull/1662) the unnecessary `this.load()`.

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

CI is running for commit e18c394efbce876da3dd412803f8c96bfc6fbe30.

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


---

Sent with 💌 from [NxCloud.](https://nx.app?utm_source=pull-request&utm_medium=comment)
SeanCassiere: Just keep track of failures logged out.
<details><summary>Details</summary>
<pre>
```
 FAIL  |react-router| tests/routeContext.test.tsx > beforeLoad in the route definition > on navigate, route context is present in the /person route after a redirect is thrown in the beforeLoad of the /about route
AssertionError: expected '/about' to be '/person' // Object.is equality

- Expected
+ Received

- /person
+ /about

 ❯ tests/routeContext.test.tsx:247:38
    245| 
    246|     await sleep(WAIT_TIME * SLEEP_MODIFIER)
    247|     expect(window.location.pathname).toBe('/person')
       |                                      ^
    248|     expect(router.state.location.pathname).toBe('/person')
    249| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/5]⎯

 FAIL  |react-router| tests/routeContext.test.tsx > beforeLoad in the route definition > on navigate, route context is present in the /person route after a redirect is thrown in the loader of the /about route
AssertionError: expected '/about' to be '/person' // Object.is equality

- Expected
+ Received

- /person
+ /about

 ❯ tests/routeContext.test.tsx:289:38
    287| 
    288|     await sleep(WAIT_TIME * SLEEP_MODIFIER)
    289|     expect(window.location.pathname).toBe('/person')
       |                                      ^
    290|     expect(router.state.location.pathname).toBe('/person')
    291| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/5]⎯

 FAIL  |react-router| tests/routeContext.test.tsx > loader in the route definition > on navigate, route context is present in the /person route after a redirect is thrown in the beforeLoad of the /about route
AssertionError: expected '/about' to be '/person' // Object.is equality

- Expected
+ Received

- /person
+ /about

 ❯ tests/routeContext.test.tsx:511:38
    509| 
    510|     await sleep(WAIT_TIME * SLEEP_MODIFIER)
    511|     expect(window.location.pathname).toBe('/person')
       |                                      ^
    512|     expect(router.state.location.pathname).toBe('/person')
    513| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/5]⎯

 FAIL  |react-router| tests/routeContext.test.tsx > loader in the route definition > on navigate, route context is present in the /person route after a redirect is thrown in the loader of the /about route
AssertionError: expected '/about' to be '/person' // Object.is equality

- Expected
+ Received

- /person
+ /about

 ❯ tests/routeContext.test.tsx:555:38
    553| 
    554|     await sleep(WAIT_TIME * SLEEP_MODIFIER)
    555|     expect(window.location.pathname).toBe('/person')
       |                                      ^
    556|     expect(router.state.location.pathname).toBe('/person')
    557| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/5]⎯

 FAIL  |react-router| tests/routeContext.test.tsx > useRouteContext in the component > on navigate, route context is present in the /person route after a redirect is thrown in the beforeLoad of the /about route
TestingLibraryElementError: Unable to find an element with the text: {"foo":"bar"}. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

Ignored nodes: comments, script, style
<body>
  <div>
    <div />
  </div>
</body>

Ignored nodes: comments, script, style
<body>
  <div>
    <div />
  </div>
</body>
 ❯ waitForWrapper ../../node_modules/.pnpm/@testing-library+dom@10.0.0/node_modules/@testing-library/dom/dist/wait-for.js:163:27
 ❯ ../../node_modules/.pnpm/@testing-library+dom@10.0.0/node_modules/@testing-library/dom/dist/query-helpers.js:86:33
 ❯ tests/routeContext.test.tsx:859:34
    857|     fireEvent.click(linkToAbout)
    858| 
    859|     const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
       |                                  ^
    860|     await waitFor(() => expect(content).toBeInTheDocument())
    861| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/5]⎯

 Test Files  1 failed | 22 passed (23)
      Tests  5 failed | 277 passed (282)
Type Errors  no errors
   Start at  12:24:18
   Duration  29.31s


```
</pre>
</details> 
SeanCassiere: The remaining tests

<details><summary>Details</summary>
<pre>
```
 FAIL  |react-router| tests/routeContext.test.tsx > beforeLoad in the route definition > on navigate, route context is present in the /person route after a redirect is thrown in the beforeLoad of the /about route
AssertionError: expected '/about' to be '/person' // Object.is equality

- Expected
+ Received

- /person
+ /about

 ❯ tests/routeContext.test.tsx:247:38
    245| 
    246|     await sleep(WAIT_TIME * SLEEP_MODIFIER)
    247|     expect(window.location.pathname).toBe('/person')
       |                                      ^
    248|     expect(router.state.location.pathname).toBe('/person')
    249| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/4]⎯

 FAIL  |react-router| tests/routeContext.test.tsx > beforeLoad in the route definition > on navigate, route context is present in the /person route after a redirect is thrown in the loader of the /about route
AssertionError: expected '/about' to be '/person' // Object.is equality

- Expected
+ Received

- /person
+ /about

 ❯ tests/routeContext.test.tsx:289:38
    287| 
    288|     await sleep(WAIT_TIME * SLEEP_MODIFIER)
    289|     expect(window.location.pathname).toBe('/person')
       |                                      ^
    290|     expect(router.state.location.pathname).toBe('/person')
    291| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/4]⎯

 FAIL  |react-router| tests/routeContext.test.tsx > loader in the route definition > on navigate, route context is present in the /person route after a redirect is thrown in the beforeLoad of the /about route
AssertionError: expected '/about' to be '/person' // Object.is equality

- Expected
+ Received

- /person
+ /about

 ❯ tests/routeContext.test.tsx:511:38
    509| 
    510|     await sleep(WAIT_TIME * SLEEP_MODIFIER)
    511|     expect(window.location.pathname).toBe('/person')
       |                                      ^
    512|     expect(router.state.location.pathname).toBe('/person')
    513| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/4]⎯

 FAIL  |react-router| tests/routeContext.test.tsx > loader in the route definition > on navigate, route context is present in the /person route after a redirect is thrown in the loader of the /about route
AssertionError: expected '/about' to be '/person' // Object.is equality

- Expected
+ Received

- /person
+ /about

 ❯ tests/routeContext.test.tsx:555:38
    553| 
    554|     await sleep(WAIT_TIME * SLEEP_MODIFIER)
    555|     expect(window.location.pathname).toBe('/person')
       |                                      ^
    556|     expect(router.state.location.pathname).toBe('/person')
    557| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/4]⎯

 Test Files  1 failed | 22 passed (23)
      Tests  4 failed | 278 passed (282)
Type Errors  no errors
   Start at  12:27:03
   Duration  29.04s


```
</pre>
</details> 
SeanCassiere: So far, I've tracked it down the `this.navigate` and subsequent `this.load` call over here in [router.ts#L1546](https://github.com/TanStack/router/blob/cb38f0bce0c42ddf68b0df4b0f4841504eb1e783/packages/react-router/src/router.ts#L1546).
SeanCassiere: For whatever reason [8a31dee](https://github.com/TanStack/router/pull/1713/commits/8a31deef54715a4b7b86a4052baba8d3ca7d3dd2), fixes the tests.

I used `useEffect` instead of `useLayoutEffect` since the router was being unsubscribed from the history being used.

The change [here in router.ts](https://github.com/TanStack/router/pull/1713/commits/8a31deef54715a4b7b86a4052baba8d3ca7d3dd2#diff-2ac234f610fa0e0c2146252c10a5266af9c5b6d0b63659a0fa4c9f2c4472596aL1911), however, is not something I fully understand yet.
SeanCassiere: More tests need to be added with layout routes.
SeanCassiere: Tested these changes against the reproductions in the following issues and can confirm that this change fixes them.

- #1730
- #1670 
- #1650 
- This [comment](https://github.com/TanStack/router/issues/1656#issuecomment-2145989592) in #1656

Context-related issues still remain broken this though.

- The original reproduction in #1656

Patch:
diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json
index 61d5b388b..378ac6aec 100644
--- a/examples/react/authenticated-routes/package.json
+++ b/examples/react/authenticated-routes/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
diff --git a/examples/react/basic-default-search-params/package.json b/examples/react/basic-default-search-params/package.json
index 6d11be8ce..e9b4760b1 100644
--- a/examples/react/basic-default-search-params/package.json
+++ b/examples/react/basic-default-search-params/package.json
@@ -10,8 +10,8 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-file-based-codesplitting/package.json b/examples/react/basic-file-based-codesplitting/package.json
index 94a7bcf6e..9b9584efe 100644
--- a/examples/react/basic-file-based-codesplitting/package.json
+++ b/examples/react/basic-file-based-codesplitting/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json
index bbe89631d..ff1f0103a 100644
--- a/examples/react/basic-file-based/package.json
+++ b/examples/react/basic-file-based/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json
index 5de7de275..3f472cc0f 100644
--- a/examples/react/basic-react-query-file-based/package.json
+++ b/examples/react/basic-react-query-file-based/package.json
@@ -11,8 +11,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.17.8",
     "@tanstack/react-query-devtools": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
diff --git a/examples/react/basic-react-query/package.json b/examples/react/basic-react-query/package.json
index 8510ded61..358309820 100644
--- a/examples/react/basic-react-query/package.json
+++ b/examples/react/basic-react-query/package.json
@@ -11,8 +11,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.17.8",
     "@tanstack/react-query-devtools": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json
index 711c6cebd..4a4d341cb 100644
--- a/examples/react/basic-ssr-file-based/package.json
+++ b/examples/react/basic-ssr-file-based/package.json
@@ -12,9 +12,9 @@
     "debug": "node --inspect-brk server"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/start": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/start": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "get-port": "^7.0.0",
diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json
index 56533bd1f..29dc3983c 100644
--- a/examples/react/basic-ssr-streaming-file-based/package.json
+++ b/examples/react/basic-ssr-streaming-file-based/package.json
@@ -12,9 +12,9 @@
     "debug": "node --inspect-brk server"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/start": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/start": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "get-port": "^7.0.0",
diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json
index 65b0816c7..119c941aa 100644
--- a/examples/react/basic/package.json
+++ b/examples/react/basic/package.json
@@ -10,8 +10,8 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
diff --git a/examples/react/deferred-data/package.json b/examples/react/deferred-data/package.json
index 15df0c29e..83c5bd5f1 100644
--- a/examples/react/deferred-data/package.json
+++ b/examples/react/deferred-data/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json
index 6a1bf039d..d7dd1bb4a 100644
--- a/examples/react/kitchen-sink-file-based/package.json
+++ b/examples/react/kitchen-sink-file-based/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
diff --git a/examples/react/kitchen-sink-react-query-file-based/package.json b/examples/react/kitchen-sink-react-query-file-based/package.json
index 2bec8d41b..4ed4e6d07 100644
--- a/examples/react/kitchen-sink-react-query-file-based/package.json
+++ b/examples/react/kitchen-sink-react-query-file-based/package.json
@@ -11,8 +11,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.17.8",
     "@tanstack/react-query-devtools": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
diff --git a/examples/react/kitchen-sink-react-query/package.json b/examples/react/kitchen-sink-react-query/package.json
index 248e3f1ce..0cee0a733 100644
--- a/examples/react/kitchen-sink-react-query/package.json
+++ b/examples/react/kitchen-sink-react-query/package.json
@@ -11,8 +11,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.17.8",
     "@tanstack/react-query-devtools": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink/package.json b/examples/react/kitchen-sink/package.json
index 23583f9a9..f84d6e2f9 100644
--- a/examples/react/kitchen-sink/package.json
+++ b/examples/react/kitchen-sink/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
     "react": "^18.2.0",
diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json
index 659b7ef78..e78e1316a 100644
--- a/examples/react/large-file-based/package.json
+++ b/examples/react/large-file-based/package.json
@@ -12,8 +12,8 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
diff --git a/examples/react/large-file-based/src/routes/__root.tsx b/examples/react/large-file-based/src/routes/__root.tsx
index cbe933d18..6407887b8 100644
--- a/examples/react/large-file-based/src/routes/__root.tsx
+++ b/examples/react/large-file-based/src/routes/__root.tsx
@@ -7,14 +7,16 @@ import {
 import { type QueryClient } from '@tanstack/react-query'
 import { TanStackRouterDevtools } from '@tanstack/router-devtools'
 
-export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()(
-  {
-    component: RootComponent,
-    notFoundComponent: () => {
-      return <p>Not Found (on root route)</p>
-    },
+export interface Context {
+  queryClient: QueryClient
+}
+
+export const Route = createRootRouteWithContext<Context>()({
+  component: RootComponent,
+  notFoundComponent: () => {
+    return <p>Not Found (on root route)</p>
   },
-)
+})
 
 function RootComponent() {
   return (
diff --git a/examples/react/large-file-based/src/routes/search/route.tsx b/examples/react/large-file-based/src/routes/search/route.tsx
index 6155a0d33..364daefb1 100644
--- a/examples/react/large-file-based/src/routes/search/route.tsx
+++ b/examples/react/large-file-based/src/routes/search/route.tsx
@@ -1,6 +1,12 @@
 import * as React from 'react'
 import { createFileRoute } from '@tanstack/react-router'
+import { z } from 'zod'
+
+const search = z.object({
+  rootSearch: z.number(),
+})
 
 export const Route = createFileRoute('/search')({
   component: () => <div>Hello /search!</div>,
+  validateSearch: search,
 })
diff --git a/examples/react/large-file-based/src/routes/search/searchPlaceholder.tsx b/examples/react/large-file-based/src/routes/search/searchPlaceholder.tsx
index c43b546e9..60372e8b9 100644
--- a/examples/react/large-file-based/src/routes/search/searchPlaceholder.tsx
+++ b/examples/react/large-file-based/src/routes/search/searchPlaceholder.tsx
@@ -44,6 +44,7 @@ function SearchComponent() {
           page: 0,
           offset: 10,
           search: 'search',
+          rootSearch: 0,
         }}
       >
         Search
diff --git a/examples/react/location-masking/package.json b/examples/react/location-masking/package.json
index b6aa0e4f9..0fbb73546 100644
--- a/examples/react/location-masking/package.json
+++ b/examples/react/location-masking/package.json
@@ -11,8 +11,8 @@
   "dependencies": {
     "@radix-ui/react-dialog": "^1.0.5",
     "@tanstack/react-query": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
diff --git a/examples/react/navigation-blocking/package.json b/examples/react/navigation-blocking/package.json
index 14f1dce4a..24173cd99 100644
--- a/examples/react/navigation-blocking/package.json
+++ b/examples/react/navigation-blocking/package.json
@@ -10,8 +10,8 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.17.8",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
diff --git a/examples/react/quickstart-file-based/package.json b/examples/react/quickstart-file-based/package.json
index 2e3ec8ec0..88a23529d 100644
--- a/examples/react/quickstart-file-based/package.json
+++ b/examples/react/quickstart-file-based/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
     "redaxios": "^0.5.0",
     "immer": "^10.0.3",
diff --git a/examples/react/quickstart/package.json b/examples/react/quickstart/package.json
index cbfa1ab41..815187679 100644
--- a/examples/react/quickstart/package.json
+++ b/examples/react/quickstart/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
   },
diff --git a/examples/react/scroll-restoration/package.json b/examples/react/scroll-restoration/package.json
index 975770233..ccecace02 100644
--- a/examples/react/scroll-restoration/package.json
+++ b/examples/react/scroll-restoration/package.json
@@ -9,9 +9,9 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
+    "@tanstack/react-router": "^1.36.2",
     "@tanstack/react-virtual": "^3.0.1",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/router-devtools": "^1.36.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
   },
diff --git a/examples/react/start-basic-rsc/package.json b/examples/react/start-basic-rsc/package.json
index 65ea3d955..a7878a187 100644
--- a/examples/react/start-basic-rsc/package.json
+++ b/examples/react/start-basic-rsc/package.json
@@ -12,10 +12,10 @@
     "format": "prettier --write '**/*' --ignore-unknown"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
-    "@tanstack/start": "^1.35.6",
+    "@tanstack/start": "^1.36.2",
     "redaxios": "^0.5.0",
     "isbot": "^3.7.0",
     "react": "^18.3.1",
diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json
index 6de3f71e7..e074d19ef 100644
--- a/examples/react/start-basic/package.json
+++ b/examples/react/start-basic/package.json
@@ -14,10 +14,10 @@
     "test:e2e:replay": "playwright test --project=replay-chromium"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
-    "@tanstack/start": "^1.35.6",
+    "@tanstack/start": "^1.36.2",
     "@typescript-eslint/parser": "^7.2.0",
     "@vitejs/plugin-react": "^4",
     "dotenv": "^16.4.5",
diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json
index b96cdc644..3d166ffd7 100644
--- a/examples/react/start-trellaux/package.json
+++ b/examples/react/start-trellaux/package.json
@@ -14,10 +14,10 @@
   "dependencies": {
     "@tanstack/react-query": "^5.40.0",
     "@tanstack/react-query-devtools": "^5.40.0",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@tanstack/router-vite-plugin": "^1.35.4",
-    "@tanstack/start": "^1.35.6",
+    "@tanstack/start": "^1.36.2",
     "@typescript-eslint/parser": "^7.2.0",
     "@vitejs/plugin-react": "^4",
     "isbot": "^3.7.0",
diff --git a/examples/react/wip-trpc-react-query/package.json b/examples/react/wip-trpc-react-query/package.json
index 52a2e59f7..ed1184687 100644
--- a/examples/react/wip-trpc-react-query/package.json
+++ b/examples/react/wip-trpc-react-query/package.json
@@ -13,8 +13,8 @@
   "dependencies": {
     "@tanstack/react-query": "^4.36.1",
     "@tanstack/react-query-devtools": "^4.36.1",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@trpc/client": "^10.45.0",
     "@trpc/react-query": "^10.45.0",
     "@trpc/server": "^10.45.0",
diff --git a/examples/react/wip-waku-rsc/package.json b/examples/react/wip-waku-rsc/package.json
index c46d9d094..0e3465ce0 100644
--- a/examples/react/wip-waku-rsc/package.json
+++ b/examples/react/wip-waku-rsc/package.json
@@ -10,8 +10,8 @@
   },
   "dependencies": {
     "@hono/node-server": "^1.4.1",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "hono": "^3.12.0",
     "react": "18.3.0-canary-0cdfef19b-20231211",
     "react-dom": "18.3.0-canary-0cdfef19b-20231211",
diff --git a/examples/react/wip-with-bling/package.json b/examples/react/wip-with-bling/package.json
index d51ea955d..65cd7c89b 100644
--- a/examples/react/wip-with-bling/package.json
+++ b/examples/react/wip-with-bling/package.json
@@ -13,10 +13,10 @@
   },
   "dependencies": {
     "@tanstack/bling": "^0.5.0",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/start": "^1.35.6",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/start": "^1.36.2",
     "@tanstack/react-store": "^0.2.1",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "fastify": "^4.25.2",
     "get-port": "^7.0.0",
diff --git a/examples/react/wip-with-framer-motion/package.json b/examples/react/wip-with-framer-motion/package.json
index bef18198f..30899fccd 100644
--- a/examples/react/wip-with-framer-motion/package.json
+++ b/examples/react/wip-with-framer-motion/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "redaxios": "^0.5.0",
     "framer-motion": "^10.17.9",
     "immer": "^10.0.3",
diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json
index bd9966704..cab5a95bf 100644
--- a/examples/react/with-trpc/package.json
+++ b/examples/react/with-trpc/package.json
@@ -13,8 +13,8 @@
   "dependencies": {
     "@tanstack/react-query": "^4.36.1",
     "@tanstack/react-query-devtools": "^4.36.1",
-    "@tanstack/react-router": "^1.35.6",
-    "@tanstack/router-devtools": "^1.36.0",
+    "@tanstack/react-router": "^1.36.2",
+    "@tanstack/router-devtools": "^1.36.2",
     "@trpc/client": "^10.45.0",
     "@trpc/react-query": "^10.45.0",
     "@trpc/server": "^10.45.0",
diff --git a/packages/react-router/package.json b/packages/react-router/package.json
index 9bdcfcc5a..e795e3a20 100644
--- a/packages/react-router/package.json
+++ b/packages/react-router/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/react-router",
-  "version": "1.35.6",
+  "version": "1.36.2",
   "description": "",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/react-router/src/Transitioner.tsx b/packages/react-router/src/Transitioner.tsx
index 412d6eaf3..e181c66ee 100644
--- a/packages/react-router/src/Transitioner.tsx
+++ b/packages/react-router/src/Transitioner.tsx
@@ -29,7 +29,7 @@ export function Transitioner() {
 
   // Subscribe to location changes
   // and try to load the new location
-  useLayoutEffect(() => {
+  React.useEffect(() => {
     const unsub = router.history.subscribe(router.load)
 
     const nextLocation = router.buildLocation({
diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx
index 80a3c0230..230a2996d 100644
--- a/packages/react-router/src/link.tsx
+++ b/packages/react-router/src/link.tsx
@@ -542,10 +542,6 @@ export function useLinkProps<
   options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
 ): React.AnchorHTMLAttributes<HTMLAnchorElement> {
   const router = useRouter()
-  const matchPathname = useMatch({
-    strict: false,
-    select: (s) => s.pathname,
-  })
   const [isTransitioning, setIsTransitioning] = React.useState(false)
 
   const {
diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts
index d72bbb294..c0e581736 100644
--- a/packages/react-router/src/router.ts
+++ b/packages/react-router/src/router.ts
@@ -1101,12 +1101,22 @@ export class Router<
       } = {},
       matches?: Array<MakeRouteMatch<TRouteTree>>,
     ): ParsedLocation => {
-      const latestLocation =
-        dest._fromLocation ?? (this.latestLocation as ParsedLocation)
-      let fromPath = latestLocation.pathname
-      let fromSearch = dest.fromSearch || latestLocation.search
+      // if the router is loading the previous location is what
+      // we should use because the old matches are still around
+      // and the latest location has already been updated.
+      // If the router is not loading we should always use the
+      // latest location because resolvedLocation can lag behind
+      // and the new matches could already render
+      const currentLocation = this.state.isLoading
+        ? this.state.resolvedLocation
+        : this.latestLocation
 
-      const fromMatches = this.matchRoutes(latestLocation.pathname, fromSearch)
+      const location = dest._fromLocation ?? currentLocation
+
+      let fromPath = location.pathname
+      let fromSearch = dest.fromSearch || location.search
+
+      const fromMatches = this.matchRoutes(location.pathname, fromSearch)
 
       const fromMatch =
         dest.from != null
@@ -1548,7 +1558,7 @@ export class Router<
           redirect = err
           if (!this.isServer) {
             this.navigate({ ...err, replace: true, __isRedirect: true })
-            this.load()
+            // this.load()
           }
         } else if (isNotFound(err)) {
           notFound = err
@@ -1773,28 +1783,32 @@ export class Router<
                   parentMatch?.context ?? this.options.context ?? {}
 
                 // Make sure the match has parent context set before going further
-                matches[index] = match = updateMatch(match.id, () => ({
+                matches[index] = match = {
                   ...match,
                   routeContext: replaceEqualDeep(
                     match.routeContext,
                     parentContext,
                   ),
+                  context: replaceEqualDeep(match.context, parentContext),
                   abortController,
-                }))
+                }
+
+                const beforeLoadFnContext = {
+                  search: match.search,
+                  abortController,
+                  params: match.params,
+                  preload: !!preload,
+                  context: match.routeContext,
+                  location,
+                  navigate: (opts: any) =>
+                    this.navigate({ ...opts, _fromLocation: location }),
+                  buildLocation: this.buildLocation,
+                  cause: preload ? 'preload' : match.cause,
+                }
 
-                const beforeLoadContext =
-                  (await route.options.beforeLoad?.({
-                    search: match.search,
-                    abortController,
-                    params: match.params,
-                    preload: !!preload,
-                    context: parentContext,
-                    location,
-                    navigate: (opts: any) =>
-                      this.navigate({ ...opts, _fromLocation: location }),
-                    buildLocation: this.buildLocation,
-                    cause: preload ? 'preload' : match.cause,
-                  })) ?? ({} as any)
+                const beforeLoadContext = route.options.beforeLoad
+                  ? (await route.options.beforeLoad(beforeLoadFnContext)) ?? {}
+                  : {}
 
                 checkLatest()
 
@@ -1850,7 +1864,7 @@ export class Router<
                   route,
                 }
 
-                const fetch = async () => {
+                const fetchAndResolveInLoaderLifetime = async () => {
                   const existing = getRouteMatch(this.state, match.id)!
                   let lazyPromise = Promise.resolve()
                   let componentsPromise = Promise.resolve() as Promise<any>
@@ -2025,7 +2039,7 @@ export class Router<
 
                 const fetchWithRedirectAndNotFound = async () => {
                   try {
-                    await fetch()
+                    await fetchAndResolveInLoaderLifetime()
                   } catch (err) {
                     checkLatest()
                     handleRedirectAndNotFound(match, err)
diff --git a/packages/react-router/src/utils.ts b/packages/react-router/src/utils.ts
index d47202f98..b3a74f5e2 100644
--- a/packages/react-router/src/utils.ts
+++ b/packages/react-router/src/utils.ts
@@ -51,7 +51,9 @@ export type Assign<TLeft, TRight> = keyof TLeft extends never
   ? TRight
   : keyof TRight extends never
     ? TLeft
-    : Omit<TLeft, keyof TRight> & TRight
+    : keyof TLeft & keyof TRight extends never
+      ? TLeft & TRight
+      : Omit<TLeft, keyof TRight> & TRight
 
 export type Timeout = ReturnType<typeof setTimeout>
 
diff --git a/packages/router-devtools/package.json b/packages/router-devtools/package.json
index 023f22006..2f63176ae 100644
--- a/packages/router-devtools/package.json
+++ b/packages/router-devtools/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-devtools",
-  "version": "1.36.0",
+  "version": "1.36.2",
   "description": "",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/start/package.json b/packages/start/package.json
index 2097cf024..497c9fea4 100644
--- a/packages/start/package.json
+++ b/packages/start/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/start",
-  "version": "1.35.6",
+  "version": "1.36.2",
   "description": "",
   "author": "Tanner Linsley",
   "license": "MIT",


Test patch:
diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx
index 0fd39865a..26836329f 100644
--- a/packages/react-router/tests/link.test.tsx
+++ b/packages/react-router/tests/link.test.tsx
@@ -1093,7 +1093,11 @@ describe('Link', () => {
   })
 
   test('when navigating from /posts/$postId to /posts/$postId/info and the current route is /posts/$postId/details', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -1222,10 +1226,16 @@ describe('Link', () => {
     expect(window.location.pathname).toEqual('/posts/id1/info')
 
     expect(await screen.findByText('Params: id1'))
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when navigating from /posts/$postId with a trailing slash to /posts/$postId/info and the current route is /posts/$postId/details', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -1354,10 +1364,16 @@ describe('Link', () => {
     expect(window.location.pathname).toEqual('/posts/id1/info')
 
     expect(await screen.findByText('Params: id1'))
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when navigating from /posts/$postId to ./info and the current route is /posts/$postId/details', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -1486,10 +1502,136 @@ describe('Link', () => {
     expect(window.location.pathname).toEqual('/posts/id1/info')
 
     expect(await screen.findByText('Params: id1'))
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
+  })
+
+  test('when navigating from /posts/$postId to / and the current route is /posts/$postId/details', async () => {
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <React.Fragment>
+            <h1>Index</h1>
+            <Link to="/posts">Posts</Link>
+            <Link to="/posts/$postId/details" params={{ postId: 'id1' }}>
+              To first post
+            </Link>
+          </React.Fragment>
+        )
+      },
+    })
+
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      component: () => {
+        return (
+          <>
+            <h1>Layout</h1>
+            <Outlet />
+          </>
+        )
+      },
+    })
+
+    const PostsComponent = () => {
+      return (
+        <React.Fragment>
+          <h1>Posts</h1>
+          <Outlet />
+        </React.Fragment>
+      )
+    }
+
+    const postsRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: 'posts',
+      component: PostsComponent,
+    })
+
+    const PostComponent = () => {
+      const params = useParams({ strict: false })
+      return (
+        <React.Fragment>
+          <span>Params: {params.postId}</span>
+          <Outlet />
+        </React.Fragment>
+      )
+    }
+
+    const postRoute = createRoute({
+      getParentRoute: () => postsRoute,
+      path: '$postId',
+      component: PostComponent,
+    })
+
+    const DetailsComponent = () => {
+      return (
+        <>
+          <h1>Details!</h1>
+          <Link from="/posts/$postId" to="/">
+            To Root
+          </Link>
+        </>
+      )
+    }
+
+    const detailsRoute = createRoute({
+      getParentRoute: () => postRoute,
+      path: 'details',
+      component: DetailsComponent,
+    })
+
+    const router = createRouter({
+      routeTree: rootRoute.addChildren([
+        indexRoute,
+        layoutRoute.addChildren([
+          postsRoute.addChildren([postRoute.addChildren([detailsRoute])]),
+        ]),
+      ]),
+    })
+
+    render(<RouterProvider router={router} />)
+
+    const postsLink = await screen.findByRole('link', { name: 'To first post' })
+
+    expect(postsLink).toHaveAttribute('href', '/posts/id1/details')
+
+    fireEvent.click(postsLink)
+
+    expect(await screen.findByText('Params: id1')).toBeInTheDocument()
+
+    expect(window.location.pathname).toEqual('/posts/id1/details')
+
+    const rootLink = await screen.findByRole('link', {
+      name: 'To Root',
+    })
+
+    expect(rootLink).toHaveAttribute('href', '/')
+
+    fireEvent.click(rootLink)
+
+    expect(await screen.findByText('Index')).toBeInTheDocument()
+
+    expect(window.location.pathname).toEqual('/')
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when navigating from /posts/$postId with search and to ./info with search and the current route is /posts/$postId/details', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -1631,10 +1773,16 @@ describe('Link', () => {
     expect(window.location.pathname).toEqual('/posts/id1/info')
 
     expect(await screen.findByText('Params: id1'))
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when navigating from /posts/$postId to ../$postId and the current route is /posts/$postId/details', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -1761,10 +1909,16 @@ describe('Link', () => {
     expect(await screen.findByText('Posts')).toBeInTheDocument()
 
     expect(window.location.pathname).toEqual('/posts/id1')
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when navigating from /posts/$postId with an index to ../$postId and the current route is /posts/$postId/details', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -1901,6 +2055,8 @@ describe('Link', () => {
     expect(await screen.findByText('Posts')).toBeInTheDocument()
 
     expect(window.location.pathname).toEqual('/posts/id1')
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when navigating from /invoices to ./invoiceId and the current route is /posts/$postId/details', async () => {
@@ -2055,7 +2211,11 @@ describe('Link', () => {
   })
 
   test('when navigating to /posts/$postId/info which is declaratively masked as /posts/$postId', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -2140,10 +2300,16 @@ describe('Link', () => {
     })
 
     expect(informationLink).toHaveAttribute('href', '/posts/id1')
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when navigating to /posts/$postId/info which is imperatively masked as /posts/$postId', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -2225,10 +2391,16 @@ describe('Link', () => {
     })
 
     expect(informationLink).toHaveAttribute('href', '/posts/id1')
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when preloading /post/$postId with a redirects to /login', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -2351,10 +2523,16 @@ describe('Link', () => {
     fireEvent.click(postLink)
 
     expect(await screen.findByText('Login!')).toBeInTheDocument()
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when preloading /post/$postId with a beforeLoad that navigates to /login', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -2443,10 +2621,16 @@ describe('Link', () => {
     fireEvent.click(postLink)
 
     expect(await screen.findByText('Login!')).toBeInTheDocument()
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 
   test('when preloading /post/$postId with a loader that navigates to /login', async () => {
-    const rootRoute = createRootRoute()
+    const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
+
+    const rootRoute = createRootRoute({
+      errorComponent: ErrorComponent,
+    })
 
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -2537,6 +2721,8 @@ describe('Link', () => {
     fireEvent.click(postLink)
 
     expect(await screen.findByText('Login!')).toBeInTheDocument()
+
+    expect(ErrorComponent).not.toHaveBeenCalled()
   })
 })
 
diff --git a/packages/react-router/tests/loaders.test.tsx b/packages/react-router/tests/loaders.test.tsx
new file mode 100644
index 000000000..24cc5bff7
--- /dev/null
+++ b/packages/react-router/tests/loaders.test.tsx
@@ -0,0 +1,245 @@
+import React, { act } from 'react'
+import '@testing-library/jest-dom/vitest'
+import {
+  cleanup,
+  configure,
+  fireEvent,
+  render,
+  screen,
+} from '@testing-library/react'
+
+import { afterEach, describe, expect, test, vi } from 'vitest'
+
+import {
+  Link,
+  RouterProvider,
+  createRootRoute,
+  createRoute,
+  createRouter,
+  redirect,
+} from '../src'
+
+import { sleep } from './utils'
+
+afterEach(() => {
+  vi.clearAllMocks()
+  vi.resetAllMocks()
+  window.history.replaceState(null, 'root', '/')
+  cleanup()
+})
+
+const WAIT_TIME = 100
+
+describe('loaders are being called', () => {
+  configure({ reactStrictMode: true })
+
+  test('called on /', async () => {
+    const indexLoaderMock = vi.fn()
+
+    const rootRoute = createRootRoute({})
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        indexLoaderMock('foo')
+      },
+      component: () => <div>Index page</div>,
+    })
+    const routeTree = rootRoute.addChildren([indexRoute])
+    const router = await act(() => createRouter({ routeTree }))
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/')
+    expect(window.location.pathname).toBe('/')
+
+    expect(indexLoaderMock).toHaveBeenCalled()
+  })
+
+  test('both are called on /nested/foo', async () => {
+    const nestedLoaderMock = vi.fn()
+    const nestedFooLoaderMock = vi.fn()
+
+    const rootRoute = createRootRoute({})
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/nested/foo">link to foo</Link>
+          </div>
+        )
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        nestedLoaderMock('nested')
+      },
+    })
+    const fooRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/foo',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        nestedFooLoaderMock('foo')
+      },
+      component: () => <div>Nested Foo page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([fooRoute]),
+      indexRoute,
+    ])
+    const router = await act(() => createRouter({ routeTree }))
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByText('link to foo')
+    await act(() => fireEvent.click(linkToAbout))
+
+    const fooElement = await screen.findByText('Nested Foo page')
+    expect(fooElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/nested/foo')
+    expect(window.location.pathname).toBe('/nested/foo')
+
+    expect(nestedLoaderMock).toHaveBeenCalled()
+    expect(nestedFooLoaderMock).toHaveBeenCalled()
+  })
+
+  test('both are called on /nested/foo when redirected in "beforeLoad" from /about', async () => {
+    const nestedLoaderMock = vi.fn()
+    const nestedFooLoaderMock = vi.fn()
+
+    const rootRoute = createRootRoute({})
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/nested/foo' })
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        nestedLoaderMock('nested')
+      },
+    })
+    const fooRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/foo',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        nestedFooLoaderMock('foo')
+      },
+      component: () => <div>Nested Foo page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([fooRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() => createRouter({ routeTree }))
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByText('link to about')
+    await act(() => fireEvent.click(linkToAbout))
+
+    const fooElement = await screen.findByText('Nested Foo page')
+    expect(fooElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/nested/foo')
+    expect(window.location.pathname).toBe('/nested/foo')
+
+    expect(nestedLoaderMock).toHaveBeenCalled()
+    expect(nestedFooLoaderMock).toHaveBeenCalled()
+  })
+
+  test('both are called on /nested/foo when redirected in "loader" from /about', async () => {
+    const nestedLoaderMock = vi.fn()
+    const nestedFooLoaderMock = vi.fn()
+
+    const rootRoute = createRootRoute({})
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/nested/foo' })
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        nestedLoaderMock('nested')
+      },
+    })
+    const fooRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/foo',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        nestedFooLoaderMock('foo')
+      },
+      component: () => <div>Nested Foo page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([fooRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() => createRouter({ routeTree }))
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByText('link to about')
+    await act(() => fireEvent.click(linkToAbout))
+
+    const fooElement = await screen.findByText('Nested Foo page')
+    expect(fooElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/nested/foo')
+    expect(window.location.pathname).toBe('/nested/foo')
+
+    expect(nestedLoaderMock).toHaveBeenCalled()
+    expect(nestedFooLoaderMock).toHaveBeenCalled()
+  })
+})
diff --git a/packages/react-router/tests/redirects.test.tsx b/packages/react-router/tests/redirects.test.tsx
index 5bfaea596..6f0d2eb95 100644
--- a/packages/react-router/tests/redirects.test.tsx
+++ b/packages/react-router/tests/redirects.test.tsx
@@ -3,15 +3,11 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
 import {
   createMemoryHistory,
   createRootRoute,
-  createRootRouteWithContext,
   createRoute,
   createRouter,
-  redirect,
   type RouterHistory,
 } from '../src'
 
-const mockFn1 = vi.fn()
-
 afterEach(() => {
   vi.clearAllMocks()
 })
@@ -463,88 +459,3 @@ describe('router.navigate navigation using layout routes resolves correctly', as
     expect(router.state.location.pathname).toBe('/g/tkdodo')
   })
 })
-
-function createContextRouter<TCtx extends Record<string, unknown>>(
-  initialEntries: string[],
-  mode: 'BEFORE_LOAD' | 'LOADER',
-  ctx: TCtx,
-) {
-  const rootRoute = createRootRouteWithContext<TCtx>()()
-
-  let allow = false
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-    beforeLoad: ({ context }) => {
-      if (!allow) {
-        allow = true
-        throw redirect({ to: '/about' })
-      }
-
-      if (mode === 'BEFORE_LOAD') {
-        mockFn1(context)
-      }
-    },
-    loader: ({ context }) => {
-      if (mode === 'LOADER') {
-        mockFn1(context)
-      }
-    },
-  })
-
-  const aboutRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/about',
-  })
-
-  const routeTree = rootRoute.addChildren([indexRoute, aboutRoute])
-
-  const router = createRouter({
-    routeTree,
-    history: createMemoryHistory({ initialEntries }),
-    context: ctx,
-  })
-
-  return router
-}
-
-describe('after redirect, the route context is passed to beforeLoad', () => {
-  it('should pass context to beforeLoad', async () => {
-    const ctx = {
-      name: 'Tanner',
-    }
-    const router = createContextRouter(['/'], 'BEFORE_LOAD', ctx)
-    await router.load()
-
-    expect(router.state.location.pathname).toBe('/about')
-    expect(mockFn1).toBeCalledTimes(0)
-
-    await router.navigate({ to: '/' })
-    await router.invalidate()
-
-    expect(router.state.location.pathname).toBe('/')
-    expect(mockFn1).toBeCalledTimes(1)
-    expect(mockFn1).toBeCalledWith(ctx)
-  })
-})
-
-describe('after redirect, the route context is passed to loader', () => {
-  it('should pass context to loader', async () => {
-    const ctx = {
-      name: 'Tanner',
-    }
-    const router = createContextRouter(['/'], 'LOADER', ctx)
-    await router.load()
-
-    expect(router.state.location.pathname).toBe('/about')
-    expect(mockFn1).toBeCalledTimes(0)
-
-    await router.navigate({ to: '/' })
-    await router.invalidate()
-
-    expect(router.state.location.pathname).toBe('/')
-    expect(mockFn1).toBeCalledTimes(1)
-    expect(mockFn1).toBeCalledWith(ctx)
-  })
-})
diff --git a/packages/react-router/tests/routeContext.test.tsx b/packages/react-router/tests/routeContext.test.tsx
index 4abe4c9f6..455020a4c 100644
--- a/packages/react-router/tests/routeContext.test.tsx
+++ b/packages/react-router/tests/routeContext.test.tsx
@@ -1,15 +1,21 @@
-import React from 'react'
+import React, { act } from 'react'
 import '@testing-library/jest-dom/vitest'
+import {
+  cleanup,
+  configure,
+  fireEvent,
+  render,
+  screen,
+} from '@testing-library/react'
 import { afterEach, describe, expect, test, vi } from 'vitest'
-import { cleanup, render, screen, waitFor } from '@testing-library/react'
 
 import {
+  Link,
+  Outlet,
+  RouterProvider,
   createRootRoute,
   createRoute,
   createRouter,
-  RouterProvider,
-  useRouteContext,
-  rootRouteId,
   redirect,
 } from '../src'
 
@@ -23,6 +29,8 @@ afterEach(() => {
 const WAIT_TIME = 150
 
 describe('beforeLoad in the route definition', () => {
+  configure({ reactStrictMode: true })
+
   // Present at the root route
   test('route context, present in the root route', async () => {
     const mock = vi.fn()
@@ -31,13 +39,18 @@ describe('beforeLoad in the route definition', () => {
       beforeLoad: ({ context }) => {
         mock(context)
       },
+      component: () => <div>Root page</div>,
     })
     const routeTree = rootRoute.addChildren([])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
+
+    const rootElement = await screen.findByText('Root page')
+    expect(rootElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
@@ -50,13 +63,18 @@ describe('beforeLoad in the route definition', () => {
         await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>Root page</div>,
     })
     const routeTree = rootRoute.addChildren([])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
 
-    render(<RouterProvider router={router} />)
+    const rootElement = await screen.findByText('Root page')
+    expect(rootElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
@@ -72,13 +90,18 @@ describe('beforeLoad in the route definition', () => {
       beforeLoad: ({ context }) => {
         mock(context)
       },
+      component: () => <div>Index page</div>,
     })
     const routeTree = rootRoute.addChildren([indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
+
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
@@ -94,13 +117,18 @@ describe('beforeLoad in the route definition', () => {
         await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>Index page</div>,
     })
     const routeTree = rootRoute.addChildren([indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
 
-    render(<RouterProvider router={router} />)
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
@@ -122,21 +150,27 @@ describe('beforeLoad in the route definition', () => {
       getParentRoute: () => rootRoute,
       path: '/',
       beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>Index page</div>,
     })
     const routeTree = rootRoute.addChildren([indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
 
-    render(<RouterProvider router={router} />)
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'sean' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
 
-  // Check if context the context is available after a redirect
-  test('route context is present in the /about route after a redirect is thrown in the beforeLoad of the index route', async () => {
+  // Check if context the context is available after a redirect on first-load
+  test('on first-load, route context is present in the /about route after a redirect is thrown in the beforeLoad of the index route', async () => {
     const mock = vi.fn()
 
     const rootRoute = createRootRoute()
@@ -152,21 +186,29 @@ describe('beforeLoad in the route definition', () => {
       getParentRoute: () => rootRoute,
       path: '/about',
       beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>About page</div>,
     })
     const routeTree = rootRoute.addChildren([aboutRoute, indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
 
-    render(<RouterProvider router={router} />)
+    const aboutElement = await screen.findByText('About page')
+    expect(aboutElement).toBeInTheDocument()
+
+    expect(window.location.pathname).toBe('/about')
+    expect(router.state.location.pathname).toBe('/about')
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
     expect(mock).toHaveBeenCalledTimes(1)
-    expect(router.state.location.pathname).toBe('/about')
   })
 
-  test('route context is present in the /about route after a redirect is thrown in the loader of the index route', async () => {
+  test('on first-load, route context is present in the /about route after a redirect is thrown in the loader of the index route', async () => {
     const mock = vi.fn()
 
     const rootRoute = createRootRoute()
@@ -182,235 +224,1515 @@ describe('beforeLoad in the route definition', () => {
       getParentRoute: () => rootRoute,
       path: '/about',
       beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>About page</div>,
     })
     const routeTree = rootRoute.addChildren([aboutRoute, indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
 
-    render(<RouterProvider router={router} />)
+    const aboutElement = await screen.findByText('About page')
+    expect(aboutElement).toBeInTheDocument()
+
+    expect(window.location.pathname).toBe('/about')
+    expect(router.state.location.pathname).toBe('/about')
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
     expect(mock).toHaveBeenCalledTimes(1)
-    expect(router.state.location.pathname).toBe('/about')
   })
-})
 
-describe('loader in the route definition', () => {
-  // Present at the root route
-  test('route context, present in the root route', async () => {
+  // Check if context the context is available after a redirect on navigate
+  test('on navigate, route context is present in the /person route after a redirect is thrown in the beforeLoad of the /about route', async () => {
     const mock = vi.fn()
 
-    const rootRoute = createRootRoute({
-      loader: ({ context }) => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/person',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>Person page</div>,
     })
-    const routeTree = rootRoute.addChildren([])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const routeTree = rootRoute.addChildren([
+      personRoute,
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
 
-    render(<RouterProvider router={router} />)
+    expect(window.location.pathname).toBe('/person')
+    expect(router.state.location.pathname).toBe('/person')
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
 
-  test('route context (sleep), present in the root route', async () => {
+  test('on navigate, route context is present in the /person route after a redirect is thrown in the loader of the /about route', async () => {
     const mock = vi.fn()
 
-    const rootRoute = createRootRoute({
-      loader: async ({ context }) => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/person',
+      beforeLoad: async ({ context }) => {
         await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>Person page</div>,
     })
-    const routeTree = rootRoute.addChildren([])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const routeTree = rootRoute.addChildren([
+      personRoute,
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
 
-    render(<RouterProvider router={router} />)
+    expect(window.location.pathname).toBe('/person')
+    expect(router.state.location.pathname).toBe('/person')
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
     expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
 
-  // Present at the index route
-  test('route context, present in the index route', async () => {
+  // Check if context returned by /nested/about, is the same as its parent route /nested on navigate
+  test('nested destination on navigate, route context in the /nested/about route is correctly inherited from the /nested parent', async () => {
     const mock = vi.fn()
 
     const rootRoute = createRootRoute()
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
       path: '/',
-      loader: ({ context }) => {
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/nested/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'nested' }
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/about',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>About page</div>,
     })
-    const routeTree = rootRoute.addChildren([indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([aboutRoute]),
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
 
-    render(<RouterProvider router={router} />)
+    const aboutElement = await screen.findByText('About page')
+    expect(aboutElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
-    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(router.state.location.href).toBe('/nested/about')
+    expect(window.location.pathname).toBe('/nested/about')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'nested' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
 
-  test('route context (sleep), present in the index route', async () => {
+  // Check if context returned by /nested/person route, is the same as its parent route /nested on navigate after a redirect /about
+  test('nested destination on navigate, when a redirect is thrown in beforeLoad in /about, the /nested/person route context is correctly inherited from its parent /nested', async () => {
     const mock = vi.fn()
 
     const rootRoute = createRootRoute()
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
       path: '/',
-      loader: async ({ context }) => {
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/nested/person' })
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'nested' }
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/person',
+      beforeLoad: async ({ context }) => {
         await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>Person page</div>,
     })
-    const routeTree = rootRoute.addChildren([indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
 
-    render(<RouterProvider router={router} />)
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
-    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(router.state.location.href).toBe('/nested/person')
+    expect(window.location.pathname).toBe('/nested/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'nested' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
 
-  // Check if context that is updated at the root, is the same in the index route
-  test('modified route context, present in the index route', async () => {
+  test('nested destination on navigate, when a redirect is thrown in loader in /about, the /nested/person route context is correctly inherited from parent /nested', async () => {
     const mock = vi.fn()
 
-    const rootRoute = createRootRoute({
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/nested/person' })
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
       beforeLoad: async ({ context }) => {
         await sleep(WAIT_TIME)
-        return {
-          ...context,
-          foo: 'sean',
-        }
+        return { ...context, layout: 'nested' }
       },
     })
-    const indexRoute = createRoute({
+    const personRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/person',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Person page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/nested/person')
+    expect(window.location.pathname).toBe('/nested/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'nested' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context returned by a layout route, is the same its child route (index route) on first-load
+  test('_layout on first-load, route context in the index route is correctly inherited from the layout parent', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const layoutRoute = createRoute({
       getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+    })
+    const indexRoute = createRoute({
+      getParentRoute: () => layoutRoute,
       path: '/',
-      loader: async ({ context }) => {
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>Index page</div>,
     })
-    const routeTree = rootRoute.addChildren([indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([indexRoute]),
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
-    expect(mock).toHaveBeenCalledWith({ foo: 'sean' })
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/')
+    expect(window.location.pathname).toBe('/')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'layout' })
     expect(mock).toHaveBeenCalledTimes(1)
   })
 
-  // Check if context the context is available after a redirect
-  test('route context is present in the /about route after a redirect is thrown in beforeLoad of the index route', async () => {
+  // Check if context returned by a layout route, is the same its child route (about route) on navigate
+  test('_layout on navigate, route context in the /about route is correctly inherited from the layout parent', async () => {
     const mock = vi.fn()
 
     const rootRoute = createRootRoute()
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
       path: '/',
-      beforeLoad: async () => {
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
         await sleep(WAIT_TIME)
-        throw redirect({ to: '/about' })
+        return { ...context, layout: 'layout' }
       },
     })
     const aboutRoute = createRoute({
-      getParentRoute: () => rootRoute,
+      getParentRoute: () => layoutRoute,
       path: '/about',
-      loader: async ({ context }) => {
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>About page</div>,
     })
-    const routeTree = rootRoute.addChildren([aboutRoute, indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([aboutRoute]),
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
 
-    render(<RouterProvider router={router} />)
+    const aboutElement = await screen.findByText('About page')
+    expect(aboutElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
-    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(router.state.location.href).toBe('/about')
+    expect(window.location.pathname).toBe('/about')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'layout' })
     expect(mock).toHaveBeenCalledTimes(1)
-    expect(router.state.location.pathname).toBe('/about')
   })
 
-  test('route context is present in the /about route after a redirect is thrown in loader of the index route', async () => {
+  // Check if context returned by a layout route, is the same its child route (about route) on after a redirect on navigate
+  test('_layout on navigate, when a redirect is thrown in beforeLoad in /about, the /person route context is correctly inherited from the layout parent', async () => {
     const mock = vi.fn()
 
     const rootRoute = createRootRoute()
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
       path: '/',
-      loader: async () => {
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
         await sleep(WAIT_TIME)
-        throw redirect({ to: '/about' })
+        throw redirect({ to: '/person' })
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/person',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Person page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/person')
+    expect(window.location.pathname).toBe('/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'layout' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  test('_layout on navigate, when a redirect is thrown in loader in /about, the /person route context is correctly inherited from the layout parent', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
       },
     })
     const aboutRoute = createRoute({
       getParentRoute: () => rootRoute,
       path: '/about',
-      loader: async ({ context }) => {
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/person',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
         mock(context)
       },
+      component: () => <div>Person page</div>,
     })
-    const routeTree = rootRoute.addChildren([aboutRoute, indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
 
-    render(<RouterProvider router={router} />)
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
 
-    await waitFor(() => expect(mock).toHaveBeenCalledOnce())
-    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(router.state.location.href).toBe('/person')
+    expect(window.location.pathname).toBe('/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'layout' })
     expect(mock).toHaveBeenCalledTimes(1)
-    expect(router.state.location.pathname).toBe('/about')
   })
 })
 
-describe('useRouteContext in the component', () => {
+describe('loader in the route definition', () => {
+  configure({ reactStrictMode: true })
+
   // Present at the root route
   test('route context, present in the root route', async () => {
+    const mock = vi.fn()
+
     const rootRoute = createRootRoute({
-      component: () => {
-        const context = useRouteContext({ from: rootRouteId })
-        return <div>{JSON.stringify(context)}</div>
+      loader: ({ context }) => {
+        mock(context)
       },
+      component: () => <div>Root page</div>,
     })
     const routeTree = rootRoute.addChildren([])
-    const ctx = { foo: 'bar' }
-    const router = createRouter({ routeTree, context: ctx })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
-    const content = await screen.findByText(JSON.stringify(ctx))
+    const rootElement = await screen.findByText('Root page')
+    expect(rootElement).toBeInTheDocument()
 
-    expect(content).toBeInTheDocument()
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(mock).toHaveBeenCalledTimes(1)
   })
 
-  test('route context (sleep in beforeLoad), present in the root route', async () => {
+  test('route context (sleep), present in the root route', async () => {
+    const mock = vi.fn()
+
     const rootRoute = createRootRoute({
-      beforeLoad: async () => {
+      loader: async ({ context }) => {
         await sleep(WAIT_TIME)
+        mock(context)
       },
-      component: () => {
-        const context = useRouteContext({ from: rootRouteId })
-        return <div>{JSON.stringify(context)}</div>
-      },
+      component: () => <div>Root page</div>,
     })
     const routeTree = rootRoute.addChildren([])
-    const ctx = { foo: 'bar' }
-    const router = createRouter({ routeTree, context: ctx })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
-    const content = await screen.findByText(JSON.stringify(ctx))
+    const rootElement = await screen.findByText('Root page')
+    expect(rootElement).toBeInTheDocument()
 
-    expect(content).toBeInTheDocument()
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Present at the index route
+  test('route context, present in the index route', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      loader: ({ context }) => {
+        mock(context)
+      },
+      component: () => <div>Index page</div>,
+    })
+    const routeTree = rootRoute.addChildren([indexRoute])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  test('route context (sleep), present in the index route', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Index page</div>,
+    })
+    const routeTree = rootRoute.addChildren([indexRoute])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context that is updated at the root, is the same in the index route
+  test('modified route context, present in the index route', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute({
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return {
+          ...context,
+          foo: 'sean',
+        }
+      },
+    })
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Index page</div>,
+    })
+    const routeTree = rootRoute.addChildren([indexRoute])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'sean' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context the context is available after a redirect on first-load
+  test('on first-load, route context is present in the /about route after a redirect is thrown in beforeLoad of the index route', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/about' })
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>About page</div>,
+    })
+    const routeTree = rootRoute.addChildren([aboutRoute, indexRoute])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const aboutElement = await screen.findByText('About page')
+    expect(aboutElement).toBeInTheDocument()
+
+    expect(window.location.pathname).toBe('/about')
+    expect(router.state.location.pathname).toBe('/about')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  test('on first-load, route context is present in the /about route after a redirect is thrown in loader of the index route', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/about' })
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>About page</div>,
+    })
+    const routeTree = rootRoute.addChildren([aboutRoute, indexRoute])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const aboutElement = await screen.findByText('About page')
+    expect(aboutElement).toBeInTheDocument()
+
+    expect(window.location.pathname).toBe('/about')
+    expect(router.state.location.pathname).toBe('/about')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context the context is available after a redirect on navigate
+  test('on navigate, route context is present in the /person route after a redirect is thrown in the beforeLoad of the /about route', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/person',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Person page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      personRoute,
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
+
+    expect(window.location.pathname).toBe('/person')
+    expect(router.state.location.pathname).toBe('/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  test('on navigate, route context is present in the /person route after a redirect is thrown in the loader of the /about route', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/person',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Person page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      personRoute,
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
+
+    expect(window.location.pathname).toBe('/person')
+    expect(router.state.location.pathname).toBe('/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context returned by a /nested/about, is the same as its parent route /nested on navigate
+  test('nested destination on navigate, route context in the /nested/about route is correctly inherited from the /nested parent', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/nested/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'nested' }
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/about',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>About page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([aboutRoute]),
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const aboutElement = await screen.findByText('About page')
+    expect(aboutElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/nested/about')
+    expect(window.location.pathname).toBe('/nested/about')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'nested' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context returned by a /nested/person route, is the same as its parent route /nested on navigate after a redirect /about
+  test('nested destination on navigate, when a redirect is thrown in beforeLoad in /about, the /nested/person route context is correctly inherited from its parent /nested', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/nested/person' })
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'nested' }
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/person',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Person page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/nested/person')
+    expect(window.location.pathname).toBe('/nested/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'nested' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  test('nested destination on navigate, when a redirect is thrown in loader in /about, the /nested/person route context is correctly inherited from parent /nested', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/nested/person' })
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'nested' }
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/person',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Person page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/nested/person')
+    expect(window.location.pathname).toBe('/nested/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'nested' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context returned by a layout route, is the same its child route (index route) on first-load
+  test('_layout on first-load, route context in the index route is correctly inherited from the layout parent', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+    })
+    const indexRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Index page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([indexRoute]),
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const indexElement = await screen.findByText('Index page')
+    expect(indexElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/')
+    expect(window.location.pathname).toBe('/')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'layout' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context returned by a layout route, is the same its child route (about route) on navigate
+  test('_layout on navigate, route context in the /about route is correctly inherited from the layout parent', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/about',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>About page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([aboutRoute]),
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const aboutElement = await screen.findByText('About page')
+    expect(aboutElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/about')
+    expect(window.location.pathname).toBe('/about')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'layout' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  // Check if context returned by a layout route, is the same its child route (about route) on after a redirect on navigate
+  test('_layout on navigate, when a redirect is thrown in beforeLoad in /about, the /person route context is correctly inherited from the layout parent', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/person',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Person page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/person')
+    expect(window.location.pathname).toBe('/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'layout' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+
+  test('_layout on navigate, when a redirect is thrown in loader in /about, the /person route context is correctly inherited from the layout parent', async () => {
+    const mock = vi.fn()
+
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        const navigate = indexRoute.useNavigate()
+        return (
+          <div>
+            <h1>Index page</h1>
+            <button
+              onClick={() => {
+                navigate({ to: '/about' })
+              }}
+            >
+              button to about
+            </button>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/person',
+      loader: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        mock(context)
+      },
+      component: () => <div>Person page</div>,
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const buttonToAbout = await screen.findByRole('button', {
+      name: 'button to about',
+    })
+    expect(buttonToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(buttonToAbout))
+
+    const personElement = await screen.findByText('Person page')
+    expect(personElement).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/person')
+    expect(window.location.pathname).toBe('/person')
+
+    expect(mock).toHaveBeenCalledWith({ foo: 'bar', layout: 'layout' })
+    expect(mock).toHaveBeenCalledTimes(1)
+  })
+})
+
+describe('useRouteContext in the component', () => {
+  configure({ reactStrictMode: true })
+
+  // Present at the root route
+  test('route context, present in the root route', async () => {
+    const rootRoute = createRootRoute({
+      component: () => {
+        const context = rootRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
+
+    expect(content).toBeInTheDocument()
+  })
+
+  test('route context (sleep in beforeLoad), present in the root route', async () => {
+    const rootRoute = createRootRoute({
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+      },
+      component: () => {
+        const context = rootRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
+
+    expect(content).toBeInTheDocument()
   })
 
   test('route context (sleep in loader), present root route', async () => {
@@ -419,17 +1741,18 @@ describe('useRouteContext in the component', () => {
         await sleep(WAIT_TIME)
       },
       component: () => {
-        const context = useRouteContext({ from: rootRouteId })
+        const context = rootRoute.useRouteContext()
         return <div>{JSON.stringify(context)}</div>
       },
     })
     const routeTree = rootRoute.addChildren([])
-    const ctx = { foo: 'bar' }
-    const router = createRouter({ routeTree, context: ctx })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
-    const content = await screen.findByText(JSON.stringify(ctx))
+    const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
 
     expect(content).toBeInTheDocument()
   })
@@ -441,17 +1764,18 @@ describe('useRouteContext in the component', () => {
       getParentRoute: () => rootRoute,
       path: '/',
       component: () => {
-        const context = useRouteContext({ from: rootRouteId })
+        const context = indexRoute.useRouteContext()
         return <div>{JSON.stringify(context)}</div>
       },
     })
     const routeTree = rootRoute.addChildren([indexRoute])
-    const ctx = { foo: 'bar' }
-    const router = createRouter({ routeTree, context: ctx })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
-    const content = await screen.findByText(JSON.stringify(ctx))
+    const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
 
     expect(content).toBeInTheDocument()
   })
@@ -465,17 +1789,18 @@ describe('useRouteContext in the component', () => {
         await sleep(WAIT_TIME)
       },
       component: () => {
-        const context = useRouteContext({ from: rootRouteId })
+        const context = indexRoute.useRouteContext()
         return <div>{JSON.stringify(context)}</div>
       },
     })
     const routeTree = rootRoute.addChildren([indexRoute])
-    const ctx = { foo: 'bar' }
-    const router = createRouter({ routeTree, context: ctx })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
-    const content = await screen.findByText(JSON.stringify(ctx))
+    const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
 
     expect(content).toBeInTheDocument()
   })
@@ -489,17 +1814,18 @@ describe('useRouteContext in the component', () => {
         await sleep(WAIT_TIME)
       },
       component: () => {
-        const context = useRouteContext({ from: rootRouteId })
+        const context = indexRoute.useRouteContext()
         return <div>{JSON.stringify(context)}</div>
       },
     })
     const routeTree = rootRoute.addChildren([indexRoute])
-    const ctx = { foo: 'bar' }
-    const router = createRouter({ routeTree, context: ctx })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
-    const content = await screen.findByText(JSON.stringify(ctx))
+    const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
 
     expect(content).toBeInTheDocument()
   })
@@ -515,22 +1841,24 @@ describe('useRouteContext in the component', () => {
         }
       },
       component: () => {
-        const context = useRouteContext({ from: rootRouteId })
+        const context = rootRoute.useRouteContext()
         return <div>{JSON.stringify(context)}</div>
       },
     })
 
     const routeTree = rootRoute.addChildren([])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
     const content = await screen.findByText(JSON.stringify({ foo: 'sean' }))
 
     expect(content).toBeInTheDocument()
   })
 
-  // Check if context that is updated at the root, is the same in the index route
+  // Check if the context that is updated at the root, is the same in the index route
   test('modified route context, present in the index route', async () => {
     const rootRoute = createRootRoute({
       beforeLoad: async ({ context }) => {
@@ -545,22 +1873,24 @@ describe('useRouteContext in the component', () => {
       getParentRoute: () => rootRoute,
       path: '/',
       component: () => {
-        const context = useRouteContext({ from: rootRouteId })
+        const context = indexRoute.useRouteContext()
         return <div>{JSON.stringify(context)}</div>
       },
     })
     const routeTree = rootRoute.addChildren([indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
     const content = await screen.findByText(JSON.stringify({ foo: 'sean' }))
 
     expect(content).toBeInTheDocument()
   })
 
-  // Check if context the context is available after a redirect
-  test('route context is present in the /about route after a redirect is thrown in the beforeLoad of the index route', async () => {
+  // Check if the context that is available after a redirect on first-load
+  test('on first-load, route context is present in the /about route after a redirect is thrown in the beforeLoad of the index route', async () => {
     const rootRoute = createRootRoute()
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -574,23 +1904,26 @@ describe('useRouteContext in the component', () => {
       getParentRoute: () => rootRoute,
       path: '/about',
       component: () => {
-        const context = useRouteContext({ from: rootRouteId })
+        const context = aboutRoute.useRouteContext()
         return <div>{JSON.stringify(context)}</div>
       },
     })
     const routeTree = rootRoute.addChildren([aboutRoute, indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
 
-    render(<RouterProvider router={router} />)
+    await act(() => render(<RouterProvider router={router} />))
 
     const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
 
     expect(router.state.location.href).toBe('/about')
     expect(window.location.pathname).toBe('/about')
+
     expect(content).toBeInTheDocument()
   })
 
-  test('route context is present in the /about route after a redirect is thrown in the loader of the index route', async () => {
+  test('on first-load, route context is present in the /about route after a redirect is thrown in the loader of the index route', async () => {
     const rootRoute = createRootRoute()
     const indexRoute = createRoute({
       getParentRoute: () => rootRoute,
@@ -604,19 +1937,552 @@ describe('useRouteContext in the component', () => {
       getParentRoute: () => rootRoute,
       path: '/about',
       component: () => {
-        const context = useRouteContext({ from: rootRouteId })
+        const context = aboutRoute.useRouteContext()
         return <div>{JSON.stringify(context)}</div>
       },
     })
     const routeTree = rootRoute.addChildren([aboutRoute, indexRoute])
-    const router = createRouter({ routeTree, context: { foo: 'bar' } })
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
+
+    expect(router.state.location.href).toBe('/about')
+    expect(window.location.pathname).toBe('/about')
+
+    expect(content).toBeInTheDocument()
+  })
+
+  // Check if the context that is available after a redirect on navigate
+  test('on navigate, route context is present in the /person route after a redirect is thrown in the beforeLoad of the /about route', async () => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/person',
+      component: () => {
+        const context = personRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      personRoute,
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByRole('link', {
+      name: 'link to about',
+    })
+    expect(linkToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(linkToAbout))
+
+    const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
+    expect(content).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/person')
+    expect(window.location.pathname).toBe('/person')
+  })
 
-    render(<RouterProvider router={router} />)
+  test('on navigate, route context is present in the /person route after a redirect is thrown in the loader of the /about route', async () => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/person',
+      component: () => {
+        const context = personRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      personRoute,
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByRole('link', {
+      name: 'link to about',
+    })
+    expect(linkToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(linkToAbout))
 
     const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
+    expect(content).toBeInTheDocument()
+
+    expect(router.state.location.href).toBe('/person')
+    expect(window.location.pathname).toBe('/person')
+  })
+
+  // Check if context returned by /nested/about, is the same its parent /nested on navigate
+  test('nested destination on navigate, route context in the /nested/about route is correctly inherited from its parent /nested', async () => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/nested/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'nested' }
+      },
+      component: () => <Outlet />,
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/about',
+      component: () => {
+        const context = aboutRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([aboutRoute]),
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByRole('link', {
+      name: 'link to about',
+    })
+    expect(linkToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(linkToAbout))
+
+    const content = await screen.findByText(
+      JSON.stringify({ foo: 'bar', layout: 'nested' }),
+    )
+
+    expect(router.state.location.href).toBe('/nested/about')
+    expect(window.location.pathname).toBe('/nested/about')
+
+    expect(content).toBeInTheDocument()
+  })
+
+  // Check if context returned by a layout route, is the same its child route (about route) on after a redirect on navigate
+  test('nested destination on navigate, when a redirect is thrown in beforeLoad in /about, the /nested/person route context is correctly inherited from its parent /nested', async () => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/nested/person' })
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'nested' }
+      },
+      component: () => <Outlet />,
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/person',
+      component: () => {
+        const context = personRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByRole('link', {
+      name: 'link to about',
+    })
+    expect(linkToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(linkToAbout))
+
+    const content = await screen.findByText(
+      JSON.stringify({ foo: 'bar', layout: 'nested' }),
+    )
+
+    expect(router.state.location.href).toBe('/nested/person')
+    expect(window.location.pathname).toBe('/nested/person')
+
+    expect(content).toBeInTheDocument()
+  })
+
+  test('nested destination on navigate, when a redirect is thrown in loader in /about, the /nested/person route context is correctly inherited from its parent /nested', async () => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/nested/person' })
+      },
+    })
+    const nestedRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/nested',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'nested' }
+      },
+      component: () => <Outlet />,
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => nestedRoute,
+      path: '/person',
+      component: () => {
+        const context = personRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      nestedRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByRole('link', {
+      name: 'link to about',
+    })
+    expect(linkToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(linkToAbout))
+
+    const content = await screen.findByText(
+      JSON.stringify({ foo: 'bar', layout: 'nested' }),
+    )
+
+    expect(router.state.location.href).toBe('/nested/person')
+    expect(window.location.pathname).toBe('/nested/person')
+
+    expect(content).toBeInTheDocument()
+  })
+
+  // Check if context returned by a layout route, is the same its child route (index route) on first-load
+  test('_layout on first-load, route context in the index route is correctly inherited from the layout parent', async () => {
+    const rootRoute = createRootRoute()
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+      component: () => <Outlet />,
+    })
+    const indexRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/',
+      component: () => {
+        const context = indexRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([indexRoute]),
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const content = await screen.findByText(
+      JSON.stringify({ foo: 'bar', layout: 'layout' }),
+    )
+
+    expect(router.state.location.href).toBe('/')
+    expect(window.location.pathname).toBe('/')
+
+    expect(content).toBeInTheDocument()
+  })
+
+  // Check if context returned by a layout route, is the same its child route (about route) on navigate
+  test('_layout on navigate, route context in the /about route is correctly inherited from the layout parent', async () => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+      component: () => <Outlet />,
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/about',
+      component: () => {
+        const context = aboutRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([aboutRoute]),
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByRole('link', {
+      name: 'link to about',
+    })
+    expect(linkToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(linkToAbout))
+
+    const content = await screen.findByText(
+      JSON.stringify({ foo: 'bar', layout: 'layout' }),
+    )
 
     expect(router.state.location.href).toBe('/about')
     expect(window.location.pathname).toBe('/about')
+
+    expect(content).toBeInTheDocument()
+  })
+
+  // Check if context returned by a layout route, is the same its child route (about route) on after a redirect on navigate
+  test('_layout on navigate, when a redirect is thrown in beforeLoad in /about, the /person route context is correctly inherited from the layout parent', async () => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      beforeLoad: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+      component: () => <Outlet />,
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/person',
+      component: () => {
+        const context = personRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByRole('link', {
+      name: 'link to about',
+    })
+    expect(linkToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(linkToAbout))
+
+    const content = await screen.findByText(
+      JSON.stringify({ foo: 'bar', layout: 'layout' }),
+    )
+
+    expect(router.state.location.href).toBe('/person')
+    expect(window.location.pathname).toBe('/person')
+
+    expect(content).toBeInTheDocument()
+  })
+
+  test('_layout on navigate, when a redirect is thrown in loader in /about, the /person route context is correctly inherited from the layout parent', async () => {
+    const rootRoute = createRootRoute()
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        return (
+          <div>
+            <h1>Index page</h1>
+            <Link to="/about">link to about</Link>
+          </div>
+        )
+      },
+    })
+    const aboutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/about',
+      loader: async () => {
+        await sleep(WAIT_TIME)
+        throw redirect({ to: '/person' })
+      },
+    })
+    const layoutRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      id: '_layout',
+      beforeLoad: async ({ context }) => {
+        await sleep(WAIT_TIME)
+        return { ...context, layout: 'layout' }
+      },
+      component: () => <Outlet />,
+    })
+    const personRoute = createRoute({
+      getParentRoute: () => layoutRoute,
+      path: '/person',
+      component: () => {
+        const context = personRoute.useRouteContext()
+        return <div>{JSON.stringify(context)}</div>
+      },
+    })
+    const routeTree = rootRoute.addChildren([
+      layoutRoute.addChildren([personRoute]),
+      aboutRoute,
+      indexRoute,
+    ])
+    const router = await act(() =>
+      createRouter({ routeTree, context: { foo: 'bar' } }),
+    )
+
+    await act(() => render(<RouterProvider router={router} />))
+
+    const linkToAbout = await screen.findByRole('link', {
+      name: 'link to about',
+    })
+    expect(linkToAbout).toBeInTheDocument()
+    await act(() => fireEvent.click(linkToAbout))
+
+    const content = await screen.findByText(
+      JSON.stringify({ foo: 'bar', layout: 'layout' }),
+    )
+
+    expect(router.state.location.href).toBe('/person')
+    expect(window.location.pathname).toBe('/person')
+
     expect(content).toBeInTheDocument()
   })
 })