Repository: TanStack/router
Pull request #2482 (2024-10-05 23:35:04)

fix(react-router): only execute context when params or loader deps change
also do not pass search params into `context`, only `loaderDeps` (in analogy to `loader`)

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

CI is running for commit c697d5c021afdd4d8907622cfd36fb3ad38707b6.

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


---

Sent with 💌 from [NxCloud.](https://nx.app?utm_source=pull-request&utm_medium=comment)
pkg-pr-new[bot]: 
[Open in Stackblitz](https://pkg.pr.new/template/5774caa5-886d-4395-a7ab-e72710d85aab)
<details><summary><b>More templates</b></summary><p>

- [tanstack-router-react-example-authenticated-routes](https://pkg.pr.new/template/46df7f5a-c968-40c7-8499-3ea15ec81dd9)
- [tanstack-router-react-example-basic](https://pkg.pr.new/template/61dde709-5e6c-4827-91f8-850ebac65f13)
- [tanstack-router-react-example-basic-default-search-params](https://pkg.pr.new/template/65aa593a-0d9a-42dd-9c17-d4a9a5216e0d)
- [tanstack-router-react-example-basic-file-based](https://pkg.pr.new/template/c297db04-abb9-4eec-b5f5-17f355b4f513)
- [tanstack-router-react-example-basic-file-based-codesplitting](https://pkg.pr.new/template/46da51f0-87f5-4fff-aa06-f93ab16b36da)
- [tanstack-router-react-example-react-query](https://pkg.pr.new/template/dc98bfd3-302c-4d6b-af97-52c109426c20)
- [tanstack-router-react-example-basic-react-query-file-based](https://pkg.pr.new/template/75508a7b-e6fe-4485-b4a8-6ae6bb16627e)
- [tanstack-router-react-example-basic-ssr-file-based](https://pkg.pr.new/template/2bcae48d-a4c2-48d1-a54e-62ab85312d8c)
- [tanstack-router-react-example-basic-ssr-streaming-file-based](https://pkg.pr.new/template/832e9ffd-1f41-49fb-bb66-ee6f04d12371)
- [tanstack-router-react-example-basic-virtual-file-based](https://pkg.pr.new/template/0e833633-a5fc-4ca2-be54-c7e4f7a3bd54)
- [tanstack-router-react-example-basic-virtual-inside-file-based](https://pkg.pr.new/template/ee8dba16-c97b-4c22-a99a-d17c765ce903)
- [tanstack-router-react-example-kitchen-sink](https://pkg.pr.new/template/e170e2a7-174a-4b7c-8666-04ecac13ff36)
- [tanstack-router-react-example-deferred-data](https://pkg.pr.new/template/7753b154-fd58-4df7-b6aa-f4300baeed07)
- [tanstack-router-react-example-kitchen-sink-file-based](https://pkg.pr.new/template/912e7ad7-16b8-4e93-a73e-7240d2972112)
- [tanstack-router-react-example-kitchen-sink-react-query](https://pkg.pr.new/template/fe61834c-53e8-4bb8-84a6-4ca8a70b7231)
- [tanstack-router-react-example-kitchen-sink-react-query-file-based](https://pkg.pr.new/template/c0911c7c-6d0d-4e10-b983-75b8305f19a9)
- [tanstack-router-react-example-large-file-based](https://pkg.pr.new/template/6bb256cd-eaac-4753-926a-fa618c5e995f)
- [tanstack-router-react-example-location-masking](https://pkg.pr.new/template/607cdf8e-3613-4bbc-be04-4d9731e47921)
- [tanstack-router-react-example-quickstart](https://pkg.pr.new/template/c617efd0-3f17-4398-bf98-56f9e76b46da)
- [tanstack-router-react-example-navigation-blocking](https://pkg.pr.new/template/e5ce2035-648b-4681-b694-18d5b2a78567)
- [tanstack-router-react-example-quickstart-file-based](https://pkg.pr.new/template/77f33573-060e-481a-843d-d90cf81cb5ab)
- [tanstack-router-react-example-quickstart-rspack-file-based](https://pkg.pr.new/template/3417fb90-0667-413a-927b-41299c11c637)
- [tanstack-router-react-example-quickstart-webpack-file-based](https://pkg.pr.new/template/502e8e5d-4059-47dd-aa8f-78bdd1eb8903)
- [tanstack-router-react-example-scroll-restoration](https://pkg.pr.new/template/63f3a892-a7a4-429c-88ae-23120e82d3e0)
- [tanstack-router-search-validator-adapters](https://pkg.pr.new/template/10bb9de1-8bb9-437c-85b2-92c454f4c8ae)
- [tanstack-start-example-basic](https://pkg.pr.new/template/690ed5cf-8840-4b94-a7f3-3bc05b8d60d5)
- [tanstack-start-example-basic-auth](https://pkg.pr.new/template/6f3717ed-07d5-4b9e-918b-c17893f06ed7)
- [tanstack-start-example-basic-react-query](https://pkg.pr.new/template/bb5373d3-f532-4051-98d9-8b4abf6d9a03)
- [tanstack-start-example-basic-rsc](https://pkg.pr.new/template/a2f9f697-dc36-434d-9c5b-890499b5dcf4)
- [tanstack-start-example-clerk-basic](https://pkg.pr.new/template/c1f5508b-e254-45bb-b51a-86726f96feb4)
- [tanstack-start-example-convex-trellaux](https://pkg.pr.new/template/63f1c0cd-dfab-4bfb-bbee-66846bf0652f)
- [tanstack-start-example-counter](https://pkg.pr.new/template/feaad240-25ed-424d-b5ae-5b31a6aa5be0)
- [tanstack-start-example-supabase-basic](https://pkg.pr.new/template/4745addb-6ade-4431-9365-519d10d719e6)
- [tanstack-start-example-trellaux](https://pkg.pr.new/template/3e369873-a95e-49fb-bb97-a2bf0550e652)
- [tanstack-router-react-example-with-framer-motion](https://pkg.pr.new/template/d1c475c6-f29c-446a-88f3-178b2517e904)
- [tanstack-router-react-example-with-trpc](https://pkg.pr.new/template/88688c29-c740-4b44-a9c8-28080a06c12f)
- [tanstack-router-react-example-with-trpc-react-query](https://pkg.pr.new/template/9f1343d0-922e-467c-8354-5bbddeacfce8)

</p></details>
      
    


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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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

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

</p></details>
      
    

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


Patch:
diff --git a/docs/config.json b/docs/config.json
index 011ae287c..5ca04d680 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -104,6 +104,10 @@
               "label": "Search Params",
               "to": "framework/react/guide/search-params"
             },
+            {
+              "label": "Link Options",
+              "to": "framework/react/guide/link-options"
+            },
             {
               "label": "Custom Links",
               "to": "framework/react/guide/custom-link"
diff --git a/docs/eslint/create-route-property-order.md b/docs/eslint/create-route-property-order.md
index fa82f1dc5..28d00fb67 100644
--- a/docs/eslint/create-route-property-order.md
+++ b/docs/eslint/create-route-property-order.md
@@ -14,9 +14,9 @@ The correct property order is as follows:
 
 - `params`
 - `validateSearch`
+- `loaderDeps`
 - `context`
 - `beforeLoad`
-- `loaderDeps`
 - `loader`
 
 All other properties are insensitive to the order as they do not depend on type inference.
diff --git a/docs/framework/react/api/router.md b/docs/framework/react/api/router.md
index c2a55bad1..2a87c7cc0 100644
--- a/docs/framework/react/api/router.md
+++ b/docs/framework/react/api/router.md
@@ -17,6 +17,7 @@ title: Router API
   - [`isNotFound`](./router/isNotFoundFunction.md)
   - [`isRedirect`](./router/isRedirectFunction.md)
   - [`lazyRouteComponent`](./router/lazyRouteComponentFunction.md)
+  - [`linkOptions`](./router/linkOptions.md)
   - [`notFound`](./router/notFoundFunction.md)
   - [`redirect`](./router/redirectFunction.md)
 - Components
diff --git a/docs/framework/react/api/router/linkOptions.md b/docs/framework/react/api/router/linkOptions.md
new file mode 100644
index 000000000..2c828500b
--- /dev/null
+++ b/docs/framework/react/api/router/linkOptions.md
@@ -0,0 +1,38 @@
+---
+id: linkOptions
+title: Link options
+---
+
+`linkOptions` is a function which type checks an object literal with the intention of being used for `Link`, `navigate` or `redirect`
+
+## linkOptions props
+
+The `linkOptions` accepts the following option:
+
+### `...props`
+
+- Type: `LinkProps & React.RefAttributes<HTMLAnchorElement>`
+- [`LinkProps`](./LinkPropsType.md)
+
+## `linkOptions` returns
+
+An object literal with the exact type inferred from the input
+
+## Examples
+
+```tsx
+const userLinkOptions = linkOptions({
+  to: '/dashboard/users/user',
+  search: {
+    usersView: {
+      sortBy: 'email',
+      filterBy: 'filter',
+    },
+    userId: 0,
+  },
+})
+
+function DashboardComponent() {
+  return <Link {...userLinkOptions} />
+}
+```
diff --git a/docs/framework/react/guide/link-options.md b/docs/framework/react/guide/link-options.md
new file mode 100644
index 000000000..f3662e520
--- /dev/null
+++ b/docs/framework/react/guide/link-options.md
@@ -0,0 +1,117 @@
+---
+title: Link Options
+---
+
+You may want to reuse options that are intended to be passed to `Link`, `redirect` or `navigate`. In which case you may decide an object literal is a good way to represent options passed to `Link`.
+
+```tsx
+const dashboardLinkOptions = {
+  to: '/dashboard',
+  search: { search: '' },
+}
+
+function DashboardComponent() {
+  return <Link {...dashboardLinkOptions} />
+}
+```
+
+There are a few problems here. `dashboardLinkOptions.to` is inferred as `string` which by default will resolve to every route when passed to `Link`, `navigate` or `redirect` (this particular issue could be fixed by `as const`). The other issue here is we do not know `dashboardLinkOptions` even passes the type checker until it is spread into `Link`. We could very easily create incorrect navigation options and only when the options are spread into `Link` do we know there is a type error.
+
+### Using `linkOptions` function to create re-usable options
+
+`linkOptions` is a function which type checks an object literal and returns the inferred input as is. This provides type safety on options exactly like `Link` before it is used allowing for easier maintenance and re-usability. Our above example using `linkOptions` looks like this:
+
+```tsx
+const dashboardLinkOptions = linkOptions({
+  to: '/dashboard',
+  search: { search: '' },
+})
+
+function DashboardComponent() {
+  return <Link {...dashboardLinkOptions} />
+}
+```
+
+This allows eager type checking of `dashboardLinkOptions` which can then be re-used anywhere
+
+```tsx
+const dashboardLinkOptions = linkOptions({
+  to: '/dashboard',
+  search: { search: '' },
+})
+
+export const Route = createFileRoute('/dashboard')({
+  component: DashboardComponent,
+  validateSearch: (input) => ({ search: input.search }),
+  beforeLoad: () => {
+    // can used in redirect
+    throw redirect(dashboardLinkOptions)
+  },
+})
+
+function DashboardComponent() {
+  const navigate = useNavigate()
+
+  return (
+    <div>
+      {/** can be used in navigate */}
+      <button onClick={() => navigate(dashboardLinkOptions)} />
+
+      {/** can be used in Link */}
+      <Link {...dashboardLinkOptions} />
+    </div>
+  )
+}
+```
+
+### An array of `linkOptions`
+
+When creating navigation you might loop over an array to construct a navigation bar. In which case `linkOptions` can be called multiple times to construct an array which can be used to render an array of `Link`'s
+
+```tsx
+const options = [
+  linkOptions({
+    to: '/dashboard',
+    label: 'Summary',
+    activeOptions: { exact: true },
+  }),
+  linkOptions({
+    to: '/dashboard/invoices',
+    label: 'Invoices',
+  }),
+  linkOptions({
+    to: '/dashboard/users',
+    label: 'Users',
+  }),
+]
+
+function DashboardComponent() {
+  return (
+    <>
+      <div className="flex items-center border-b">
+        <h2 className="text-xl p-2">Dashboard</h2>
+      </div>
+
+      <div className="flex flex-wrap divide-x">
+        {options.map((option) => {
+          return (
+            <Link
+              {...option}
+              key={option.to}
+              activeProps={{ className: `font-bold` }}
+              className="p-2"
+            >
+              {option.label}
+            </Link>
+          )
+        })}
+      </div>
+      <hr />
+
+      <Outlet />
+    </>
+  )
+}
+```
+
+The input of `linkOptions` is inferred and returned, as shown with the use of `label` as this does not exist on `Link` props
diff --git a/examples/react/authenticated-routes/package.json b/examples/react/authenticated-routes/package.json
index b613ba579..63d846968 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.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-default-search-params/package.json b/examples/react/basic-default-search-params/package.json
index 6d430b673..73660224b 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.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "redaxios": "^0.5.1",
     "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 f94e1913f..a7cd8e079 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.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-file-based/package.json b/examples/react/basic-file-based/package.json
index 106453078..0adacc770 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.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-react-query-file-based/package.json b/examples/react/basic-react-query-file-based/package.json
index 44690a24f..00acc6b7b 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.59.0",
     "@tanstack/react-query-devtools": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-react-query/package.json b/examples/react/basic-react-query/package.json
index 31c2b6fad..3f38a257a 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.59.0",
     "@tanstack/react-query-devtools": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
     "redaxios": "^0.5.1"
diff --git a/examples/react/basic-ssr-file-based/package.json b/examples/react/basic-ssr-file-based/package.json
index 4024ffc52..c6b7702af 100644
--- a/examples/react/basic-ssr-file-based/package.json
+++ b/examples/react/basic-ssr-file-based/package.json
@@ -11,10 +11,10 @@
     "debug": "node --inspect-brk server"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/start": "^1.63.3",
     "get-port": "^7.1.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-ssr-streaming-file-based/package.json b/examples/react/basic-ssr-streaming-file-based/package.json
index 4cd1e4fbe..7e71e1f17 100644
--- a/examples/react/basic-ssr-streaming-file-based/package.json
+++ b/examples/react/basic-ssr-streaming-file-based/package.json
@@ -11,10 +11,10 @@
     "debug": "node --inspect-brk server"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/start": "^1.63.3",
     "get-port": "^7.1.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/basic-virtual-file-based/package.json b/examples/react/basic-virtual-file-based/package.json
index 171abb381..9102c3a3f 100644
--- a/examples/react/basic-virtual-file-based/package.json
+++ b/examples/react/basic-virtual-file-based/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "@tanstack/virtual-file-routes": "^1.56.0",
     "react": "^18.2.0",
diff --git a/examples/react/basic-virtual-inside-file-based/package.json b/examples/react/basic-virtual-inside-file-based/package.json
index 3f308161e..ec3aa19e5 100644
--- a/examples/react/basic-virtual-inside-file-based/package.json
+++ b/examples/react/basic-virtual-inside-file-based/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "@tanstack/virtual-file-routes": "^1.56.0",
     "react": "^18.2.0",
diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json
index d76f3222e..51220b2db 100644
--- a/examples/react/basic/package.json
+++ b/examples/react/basic/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
     "redaxios": "^0.5.1"
diff --git a/examples/react/deferred-data/package.json b/examples/react/deferred-data/package.json
index be0af32ab..36ad5d5e7 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.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "redaxios": "^0.5.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/kitchen-sink-file-based/package.json b/examples/react/kitchen-sink-file-based/package.json
index b81244465..dc1f9448c 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.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink-file-based/src/routes/dashboard.tsx b/examples/react/kitchen-sink-file-based/src/routes/dashboard.tsx
index ed082a161..b718b59a2 100644
--- a/examples/react/kitchen-sink-file-based/src/routes/dashboard.tsx
+++ b/examples/react/kitchen-sink-file-based/src/routes/dashboard.tsx
@@ -1,5 +1,10 @@
 import * as React from 'react'
-import { Link, Outlet, createFileRoute } from '@tanstack/react-router'
+import {
+  Link,
+  Outlet,
+  createFileRoute,
+  linkOptions,
+} from '@tanstack/react-router'
 
 export const Route = createFileRoute('/dashboard')({
   component: DashboardComponent,
@@ -8,6 +13,22 @@ export const Route = createFileRoute('/dashboard')({
   }),
 })
 
+const options = [
+  linkOptions({
+    to: '/dashboard',
+    label: 'Summary',
+    activeOptions: { exact: true },
+  }),
+  linkOptions({
+    to: '/dashboard/invoices',
+    label: 'Invoices',
+  }),
+  linkOptions({
+    to: '/dashboard/users',
+    label: 'Users',
+  }),
+]
+
 function DashboardComponent() {
   return (
     <>
@@ -16,22 +37,15 @@ function DashboardComponent() {
       </div>
 
       <div className="flex flex-wrap divide-x">
-        {(
-          [
-            ['/dashboard', 'Summary', true],
-            ['/dashboard/invoices', 'Invoices'],
-            ['/dashboard/users', 'Users'],
-          ] as const
-        ).map(([to, label, exact]) => {
+        {options.map((option) => {
           return (
             <Link
-              key={to}
-              to={to}
-              activeOptions={{ exact }}
+              key={option.to}
+              {...option}
               activeProps={{ className: `font-bold` }}
               className="p-2"
             >
-              {label}
+              {option.label}
             </Link>
           )
         })}
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 88fb9f7af..e633cba1e 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.59.0",
     "@tanstack/react-query-devtools": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink-react-query/package.json b/examples/react/kitchen-sink-react-query/package.json
index 983dc9f3c..c36c809bf 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.59.0",
     "@tanstack/react-query-devtools": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "redaxios": "^0.5.1",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/kitchen-sink/package.json b/examples/react/kitchen-sink/package.json
index 37ea07810..d915dad75 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.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "redaxios": "^0.5.1",
     "immer": "^10.1.1",
     "react": "^18.2.0",
diff --git a/examples/react/large-file-based/package.json b/examples/react/large-file-based/package.json
index 3b7e3b23c..7c2b9376d 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.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/large-file-based/src/routes/linkProps.tsx b/examples/react/large-file-based/src/routes/linkProps.tsx
index 693fbe0a9..2811bcd5a 100644
--- a/examples/react/large-file-based/src/routes/linkProps.tsx
+++ b/examples/react/large-file-based/src/routes/linkProps.tsx
@@ -1,14 +1,14 @@
 import * as React from 'react'
-import { Link, type LinkProps, createFileRoute } from '@tanstack/react-router'
+import { Link, createFileRoute, linkOptions } from '@tanstack/react-router'
 
 export const Route = createFileRoute('/linkProps')({
   component: LinkPropsPage,
 })
 
 function LinkPropsPage() {
-  const linkProps: LinkProps = {
+  const linkProps = linkOptions({
     to: '/absolute',
-  }
+  })
 
   return <Link {...linkProps} />
 }
diff --git a/examples/react/location-masking/package.json b/examples/react/location-masking/package.json
index 8a6e9b4a7..67c491254 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.1.2",
     "@tanstack/react-query": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "redaxios": "^0.5.1",
     "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 c9c6b9a59..31e53bd0a 100644
--- a/examples/react/navigation-blocking/package.json
+++ b/examples/react/navigation-blocking/package.json
@@ -10,8 +10,8 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "redaxios": "^0.5.1",
     "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 f6df3eba2..86023e6ec 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.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/quickstart-rspack-file-based/package.json b/examples/react/quickstart-rspack-file-based/package.json
index c844a4d96..e41146e7f 100644
--- a/examples/react/quickstart-rspack-file-based/package.json
+++ b/examples/react/quickstart-rspack-file-based/package.json
@@ -8,8 +8,8 @@
     "preview": "rsbuild preview"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "react": "^18.3.1",
     "react-dom": "^18.3.1"
   },
diff --git a/examples/react/quickstart-webpack-file-based/package.json b/examples/react/quickstart-webpack-file-based/package.json
index d3e3b5e95..f462168dd 100644
--- a/examples/react/quickstart-webpack-file-based/package.json
+++ b/examples/react/quickstart-webpack-file-based/package.json
@@ -7,8 +7,8 @@
     "build": "webpack build && tsc --noEmit"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "react": "^18.3.1",
     "react-dom": "^18.3.1"
   },
diff --git a/examples/react/quickstart/package.json b/examples/react/quickstart/package.json
index 62c58b3be..01d1ea647 100644
--- a/examples/react/quickstart/package.json
+++ b/examples/react/quickstart/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.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 ee39940e7..eeee6e910 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.62.1",
+    "@tanstack/react-router": "^1.63.2",
     "@tanstack/react-virtual": "^3.10.8",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/router-devtools": "^1.63.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
   },
diff --git a/examples/react/search-validator-adapters/package.json b/examples/react/search-validator-adapters/package.json
index 35aa2566e..62eaf11b7 100644
--- a/examples/react/search-validator-adapters/package.json
+++ b/examples/react/search-validator-adapters/package.json
@@ -10,12 +10,12 @@
   },
   "dependencies": {
     "@tanstack/react-query": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-arktype-adapter": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-arktype-adapter": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
-    "@tanstack/router-valibot-adapter": "^1.62.1",
-    "@tanstack/router-zod-adapter": "^1.62.1",
+    "@tanstack/router-valibot-adapter": "^1.63.2",
+    "@tanstack/router-zod-adapter": "^1.63.2",
     "arktype": "^2.0.0-rc.14",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/react/start-basic-auth/package.json b/examples/react/start-basic-auth/package.json
index 24b06cc19..c505e6707 100644
--- a/examples/react/start-basic-auth/package.json
+++ b/examples/react/start-basic-auth/package.json
@@ -11,9 +11,9 @@
   },
   "dependencies": {
     "@prisma/client": "5.20.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "dotenv": "^16.4.5",
     "prisma": "^5.20.0",
     "react": "^18.3.1",
diff --git a/examples/react/start-basic-react-query/package.json b/examples/react/start-basic-react-query/package.json
index 2c7d38251..d56de9a57 100644
--- a/examples/react/start-basic-react-query/package.json
+++ b/examples/react/start-basic-react-query/package.json
@@ -11,10 +11,10 @@
   "dependencies": {
     "@tanstack/react-query": "^5.59.0",
     "@tanstack/react-query-devtools": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/react-router-with-query": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/react-router-with-query": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
     "redaxios": "^0.5.1",
diff --git a/examples/react/start-basic-rsc/package.json b/examples/react/start-basic-rsc/package.json
index ca3950676..fb66da15b 100644
--- a/examples/react/start-basic-rsc/package.json
+++ b/examples/react/start-basic-rsc/package.json
@@ -10,9 +10,9 @@
   },
   "dependencies": {
     "@babel/plugin-syntax-typescript": "^7.25.7",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "redaxios": "^0.5.1",
     "tailwind-merge": "^2.5.3",
     "vinxi": "0.4.3"
diff --git a/examples/react/start-basic/package.json b/examples/react/start-basic/package.json
index de1cb130c..efc41d31c 100644
--- a/examples/react/start-basic/package.json
+++ b/examples/react/start-basic/package.json
@@ -9,9 +9,9 @@
     "start": "vinxi start"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
     "redaxios": "^0.5.1",
diff --git a/examples/react/start-clerk-basic/package.json b/examples/react/start-clerk-basic/package.json
index ab1dd8083..7d8b4195c 100644
--- a/examples/react/start-clerk-basic/package.json
+++ b/examples/react/start-clerk-basic/package.json
@@ -10,9 +10,9 @@
   },
   "dependencies": {
     "@clerk/tanstack-start": "0.4.11",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "dotenv": "^16.4.5",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
diff --git a/examples/react/start-convex-trellaux/package.json b/examples/react/start-convex-trellaux/package.json
index 26f9ae172..46456b2ce 100644
--- a/examples/react/start-convex-trellaux/package.json
+++ b/examples/react/start-convex-trellaux/package.json
@@ -13,10 +13,10 @@
   "dependencies": {
     "@tanstack/react-query": "^5.59.0",
     "@tanstack/react-query-devtools": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/react-router-with-query": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/react-router-with-query": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "@convex-dev/react-query": "0.0.0-alpha.5",
     "concurrently": "^8.2.2",
     "convex": "^1.16.3",
diff --git a/examples/react/start-counter/package.json b/examples/react/start-counter/package.json
index dcb1154e8..d1b2311f0 100644
--- a/examples/react/start-counter/package.json
+++ b/examples/react/start-counter/package.json
@@ -9,8 +9,8 @@
     "start": "vinxi start"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
     "vinxi": "0.4.3"
diff --git a/examples/react/start-supabase-basic/package.json b/examples/react/start-supabase-basic/package.json
index 1b2f8ac7d..fd29af640 100644
--- a/examples/react/start-supabase-basic/package.json
+++ b/examples/react/start-supabase-basic/package.json
@@ -15,9 +15,9 @@
   "dependencies": {
     "@supabase/ssr": "^0.5.1",
     "@supabase/supabase-js": "^2.45.4",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "@vitejs/plugin-react": "^4.3.2",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
diff --git a/examples/react/start-trellaux/package.json b/examples/react/start-trellaux/package.json
index 4790f713e..e79d2ed71 100644
--- a/examples/react/start-trellaux/package.json
+++ b/examples/react/start-trellaux/package.json
@@ -11,10 +11,10 @@
   "dependencies": {
     "@tanstack/react-query": "^5.59.0",
     "@tanstack/react-query-devtools": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/react-router-with-query": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
-    "@tanstack/start": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/react-router-with-query": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
+    "@tanstack/start": "^1.63.3",
     "ky": "^1.7.2",
     "msw": "^2.4.9",
     "react": "^18.3.1",
diff --git a/examples/react/with-framer-motion/package.json b/examples/react/with-framer-motion/package.json
index 865f042ff..0dd8fb187 100644
--- a/examples/react/with-framer-motion/package.json
+++ b/examples/react/with-framer-motion/package.json
@@ -9,8 +9,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "redaxios": "^0.5.1",
     "framer-motion": "^11.11.1",
     "react": "^18.2.0",
diff --git a/examples/react/with-trpc-react-query/package.json b/examples/react/with-trpc-react-query/package.json
index 77c51f8d1..89e45f995 100644
--- a/examples/react/with-trpc-react-query/package.json
+++ b/examples/react/with-trpc-react-query/package.json
@@ -10,8 +10,8 @@
   "dependencies": {
     "@tanstack/react-query": "^5.59.0",
     "@tanstack/react-query-devtools": "^5.59.0",
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "@trpc/client": "11.0.0-rc.561",
     "@trpc/react-query": "11.0.0-rc.561",
diff --git a/examples/react/with-trpc/package.json b/examples/react/with-trpc/package.json
index 9ca07904c..ab9a40e42 100644
--- a/examples/react/with-trpc/package.json
+++ b/examples/react/with-trpc/package.json
@@ -8,8 +8,8 @@
     "start": "vinxi start"
   },
   "dependencies": {
-    "@tanstack/react-router": "^1.62.1",
-    "@tanstack/router-devtools": "^1.62.1",
+    "@tanstack/react-router": "^1.63.2",
+    "@tanstack/router-devtools": "^1.63.2",
     "@tanstack/router-plugin": "^1.62.0",
     "@trpc/client": "11.0.0-rc.561",
     "@trpc/server": "11.0.0-rc.561",
diff --git a/packages/create-router/package.json b/packages/create-router/package.json
index 43ca82202..f9581d452 100644
--- a/packages/create-router/package.json
+++ b/packages/create-router/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/create-router",
-  "version": "1.62.1",
+  "version": "1.63.2",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/eslint-plugin-router/src/rules/create-route-property-order/constants.ts b/packages/eslint-plugin-router/src/rules/create-route-property-order/constants.ts
index 1235d10b4..74885cd10 100644
--- a/packages/eslint-plugin-router/src/rules/create-route-property-order/constants.ts
+++ b/packages/eslint-plugin-router/src/rules/create-route-property-order/constants.ts
@@ -17,15 +17,15 @@ export type CreateRouteFunction = (typeof createRouteFunctions)[number]
 export const checkedProperties = [
   'params',
   'validateSearch',
+  'loaderDeps',
   'context',
   'beforeLoad',
-  'loaderDeps',
   'loader',
 ] as const
 
 export const sortRules = [
-  [['params', 'validateSearch'], ['context']],
+  [['params', 'validateSearch'], ['loaderDeps']],
+  [['loaderDeps'], ['context']],
   [['context'], ['beforeLoad']],
-  [['beforeLoad'], ['loaderDeps']],
-  [['loaderDeps'], ['loader']],
+  [['beforeLoad'], ['loader']],
 ] as const
diff --git a/packages/react-router-with-query/package.json b/packages/react-router-with-query/package.json
index cee47a058..07f380bba 100644
--- a/packages/react-router-with-query/package.json
+++ b/packages/react-router-with-query/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/react-router-with-query",
-  "version": "1.62.1",
+  "version": "1.63.2",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/react-router/package.json b/packages/react-router/package.json
index 7c314d236..68f474cbd 100644
--- a/packages/react-router/package.json
+++ b/packages/react-router/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/react-router",
-  "version": "1.62.1",
+  "version": "1.63.2",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/react-router/src/index.tsx b/packages/react-router/src/index.tsx
index fa54f0150..ad1912dc6 100644
--- a/packages/react-router/src/index.tsx
+++ b/packages/react-router/src/index.tsx
@@ -42,7 +42,7 @@ export * from './history'
 
 export { lazyRouteComponent } from './lazyRouteComponent'
 
-export { useLinkProps, createLink, Link } from './link'
+export { useLinkProps, createLink, Link, linkOptions } from './link'
 export type {
   CleanPath,
   Split,
diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx
index 952e1ba3c..c20bec5af 100644
--- a/packages/react-router/src/link.tsx
+++ b/packages/react-router/src/link.tsx
@@ -994,3 +994,21 @@ export const Link: LinkComponent<'a'> = React.forwardRef<Element, any>(
 function isCtrlEvent(e: MouseEvent) {
   return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
 }
+
+export type LinkOptionsFn<TComp> = <
+  const TProps,
+  TRouter extends AnyRouter = RegisteredRouter,
+  TFrom extends string = string,
+  TTo extends string | undefined = undefined,
+  TMaskFrom extends string = TFrom,
+  TMaskTo extends string = '',
+>(
+  options: Constrain<
+    TProps,
+    LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>
+  >,
+) => TProps
+
+export const linkOptions: LinkOptionsFn<'a'> = (options) => {
+  return options as any
+}
diff --git a/packages/react-router/src/not-found.tsx b/packages/react-router/src/not-found.tsx
index 667afef3a..9db1d473e 100644
--- a/packages/react-router/src/not-found.tsx
+++ b/packages/react-router/src/not-found.tsx
@@ -52,9 +52,13 @@ export function CatchNotFound(props: {
           throw error
         }
       }}
-      errorComponent={({ error }: { error: NotFoundError }) =>
-        props.fallback?.(error)
-      }
+      errorComponent={({ error }: { error: Error }) => {
+        if (isNotFound(error)) {
+          return props.fallback?.(error)
+        } else {
+          throw error
+        }
+      }}
     >
       {props.children}
     </CatchBoundary>
diff --git a/packages/react-router/src/path.ts b/packages/react-router/src/path.ts
index c77ea0987..7569ba31b 100644
--- a/packages/react-router/src/path.ts
+++ b/packages/react-router/src/path.ts
@@ -298,6 +298,10 @@ export function matchByPath(
   from: string,
   matchLocation: Pick<MatchLocation, 'to' | 'caseSensitive' | 'fuzzy'>,
 ): Record<string, string> | undefined {
+  // check basepath first
+  if (basepath !== '/' && !from.startsWith(basepath)) {
+    return undefined
+  }
   // Remove the base path from the pathname
   from = removeBasepath(basepath, from)
   // Default to to $ (wildcard)
diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts
index 6dd59a8a4..002bfd9b3 100644
--- a/packages/react-router/src/route.ts
+++ b/packages/react-router/src/route.ts
@@ -201,9 +201,9 @@ export type FileBaseRouteOptions<
     (
       ctx: RouteContextOptions<
         TParentRoute,
-        TSearchValidator,
         TParams,
-        TRouterContext
+        TRouterContext,
+        TLoaderDeps
       >,
     ) => any
   >
@@ -272,9 +272,8 @@ export type BaseRouteOptions<
 
 export interface ContextOptions<
   in out TParentRoute extends AnyRoute,
-  in out TSearchValidator,
   in out TParams,
-> extends FullSearchSchemaOption<TParentRoute, TSearchValidator> {
+> {
   abortController: AbortController
   preload: boolean
   params: Expand<ResolveAllParamsFromParent<TParentRoute, TParams>>
@@ -290,10 +289,11 @@ export interface ContextOptions<
 
 export interface RouteContextOptions<
   in out TParentRoute extends AnyRoute,
-  in out TSearchValidator,
   in out TParams,
   in out TRouterContext,
-> extends ContextOptions<TParentRoute, TSearchValidator, TParams> {
+  in out TLoaderDeps,
+> extends ContextOptions<TParentRoute, TParams> {
+  deps: TLoaderDeps
   context: Expand<RouteContextParameter<TParentRoute, TRouterContext>>
 }
 
@@ -303,7 +303,8 @@ export interface BeforeLoadContextOptions<
   in out TParams,
   in out TRouterContext,
   in out TRouteContextFn,
-> extends ContextOptions<TParentRoute, TSearchValidator, TParams> {
+> extends ContextOptions<TParentRoute, TParams>,
+    FullSearchSchemaOption<TParentRoute, TSearchValidator> {
   context: Expand<
     BeforeLoadContextParameter<TParentRoute, TRouterContext, TRouteContextFn>
   >
diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts
index 8d7834852..6c795c45e 100644
--- a/packages/react-router/src/router.ts
+++ b/packages/react-router/src/router.ts
@@ -82,8 +82,6 @@ import type { NotFoundError } from './not-found'
 import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
 import type { RouterTransformer } from './transformer'
 
-//
-
 declare global {
   interface Window {
     __TSR__?: {
@@ -600,7 +598,11 @@ export function createRouter<
   >(options)
 }
 
-type MatchRoutesOpts = { preload?: boolean; throwOnError?: boolean }
+type MatchRoutesOpts = {
+  preload?: boolean
+  throwOnError?: boolean
+  _buildLocation?: boolean
+}
 
 export class Router<
   in out TRouteTree extends AnyRoute,
@@ -991,10 +993,8 @@ export class Router<
 
   public matchRoutes(
     pathnameOrNext: string | ParsedLocation,
-    locationSearchOrOpts?:
-      | AnySearchSchema
-      | { preload?: boolean; throwOnError?: boolean },
-    opts?: { preload?: boolean; throwOnError?: boolean },
+    locationSearchOrOpts?: AnySearchSchema | MatchRoutesOpts,
+    opts?: MatchRoutesOpts,
   ) {
     if (typeof pathnameOrNext === 'string') {
       return this.matchRoutesInternal(
@@ -1011,7 +1011,7 @@ export class Router<
 
   private matchRoutesInternal(
     next: ParsedLocation,
-    opts?: { preload?: boolean; throwOnError?: boolean },
+    opts?: MatchRoutesOpts,
   ): Array<AnyRouteMatch> {
     let routeParams: Record<string, string> = {}
 
@@ -1081,9 +1081,6 @@ export class Router<
       return rootRouteId
     })()
 
-    // Existing matches are matches that are already loaded along with
-    // pending matches that are still loading
-
     const parseErrors = matchedRoutes.map((route) => {
       let parsedParamsError
 
@@ -1182,6 +1179,9 @@ export class Router<
       // Waste not, want not. If we already have a match for this route,
       // reuse it. This is important for layout routes, which might stick
       // around between navigation actions that only change leaf routes.
+
+      // Existing matches are matches that are already loaded along with
+      // pending matches that are still loading
       const existingMatch = this.getMatch(matchId)
 
       const cause = this.state.matches.find((d) => d.id === matchId)
@@ -1274,28 +1274,31 @@ export class Router<
         ...match.__beforeLoadContext,
       }
 
-      // Update the match's context
-      const contextFnContext: RouteContextOptions<any, any, any, any> = {
-        search: match.search,
-        params: match.params,
-        context: match.context,
-        location: next,
-        navigate: (opts: any) =>
-          this.navigate({ ...opts, _fromLocation: next }),
-        buildLocation: this.buildLocation,
-        cause: match.cause,
-        abortController: match.abortController,
-        preload: !!match.preload,
-        matches,
-      }
+      // only execute `context` if we are not just building a location
+      if (!existingMatch && opts?._buildLocation !== true) {
+        // Update the match's context
+        const contextFnContext: RouteContextOptions<any, any, any, any> = {
+          deps: loaderDeps,
+          params: match.params,
+          context: match.context,
+          location: next,
+          navigate: (opts: any) =>
+            this.navigate({ ...opts, _fromLocation: next }),
+          buildLocation: this.buildLocation,
+          cause: match.cause,
+          abortController: match.abortController,
+          preload: !!match.preload,
+          matches,
+        }
 
-      // Get the route context
-      match.__routeContext = route.options.context?.(contextFnContext) ?? {}
+        // Get the route context
+        match.__routeContext = route.options.context?.(contextFnContext) ?? {}
 
-      match.context = {
-        ...parentContext,
-        ...match.__routeContext,
-        ...match.__beforeLoadContext,
+        match.context = {
+          ...parentContext,
+          ...match.__routeContext,
+          ...match.__beforeLoadContext,
+        }
       }
 
       matches.push(match)
@@ -1327,7 +1330,7 @@ export class Router<
       matches?: Array<MakeRouteMatch<TRouteTree>>,
     ): ParsedLocation => {
       const fromMatches = dest._fromLocation
-        ? this.matchRoutes(dest._fromLocation)
+        ? this.matchRoutes(dest._fromLocation, { _buildLocation: true })
         : this.state.matches
 
       const fromMatch =
@@ -1506,9 +1509,9 @@ export class Router<
         }
       }
 
-      const nextMatches = this.matchRoutes(next)
+      const nextMatches = this.matchRoutes(next, { _buildLocation: true })
       const maskedMatches = maskedNext
-        ? this.matchRoutes(maskedNext)
+        ? this.matchRoutes(maskedNext, { _buildLocation: true })
         : undefined
       const maskedFinal = maskedNext
         ? build(maskedDest, maskedMatches)
@@ -2254,10 +2257,8 @@ export class Router<
                             route._lazyPromise ||
                             (route.lazyFn
                               ? route.lazyFn().then((lazyRoute) => {
-                                  Object.assign(
-                                    route.options,
-                                    lazyRoute.options,
-                                  )
+                                  const { id, ...options } = lazyRoute.options
+                                  Object.assign(route.options, options)
                                 })
                               : Promise.resolve())
 
diff --git a/packages/router-arktype-adapter/package.json b/packages/router-arktype-adapter/package.json
index 44e557b04..a12df0275 100644
--- a/packages/router-arktype-adapter/package.json
+++ b/packages/router-arktype-adapter/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-arktype-adapter",
-  "version": "1.62.1",
+  "version": "1.63.2",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/router-devtools/package.json b/packages/router-devtools/package.json
index 0cd85a923..38bd29191 100644
--- a/packages/router-devtools/package.json
+++ b/packages/router-devtools/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-devtools",
-  "version": "1.62.1",
+  "version": "1.63.2",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/router-valibot-adapter/package.json b/packages/router-valibot-adapter/package.json
index 891a0a56d..bdec74bbb 100644
--- a/packages/router-valibot-adapter/package.json
+++ b/packages/router-valibot-adapter/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-valibot-adapter",
-  "version": "1.62.1",
+  "version": "1.63.2",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/router-zod-adapter/package.json b/packages/router-zod-adapter/package.json
index 1c10b0d16..5b8f85354 100644
--- a/packages/router-zod-adapter/package.json
+++ b/packages/router-zod-adapter/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/router-zod-adapter",
-  "version": "1.62.1",
+  "version": "1.63.2",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/start/package.json b/packages/start/package.json
index 2851bc9e4..8b5fa33d8 100644
--- a/packages/start/package.json
+++ b/packages/start/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/start",
-  "version": "1.62.1",
+  "version": "1.63.3",
   "description": "Modern and scalable routing for React applications",
   "author": "Tanner Linsley",
   "license": "MIT",
diff --git a/packages/start/src/config/index.ts b/packages/start/src/config/index.ts
index 26ed08000..0b3738730 100644
--- a/packages/start/src/config/index.ts
+++ b/packages/start/src/config/index.ts
@@ -257,6 +257,7 @@ export function defineConfig(
       ...serverOptions,
       preset: deploymentPreset,
       experimental: {
+        ...serverOptions.experimental,
         asyncContext: true,
       },
     },


Test patch:
diff --git a/packages/react-router/tests/link.test-d.tsx b/packages/react-router/tests/link.test-d.tsx
index f4527e76f..86b07d565 100644
--- a/packages/react-router/tests/link.test-d.tsx
+++ b/packages/react-router/tests/link.test-d.tsx
@@ -5,6 +5,7 @@ import {
   createRootRoute,
   createRoute,
   createRouter,
+  linkOptions,
 } from '../src'
 import type { CreateLinkProps, SearchSchemaInput } from '../src'
 
@@ -3839,3 +3840,288 @@ test('when passing a component with props to createLink and navigating to the ro
 
   createLink((props) => expectTypeOf(props).toEqualTypeOf<CreateLinkProps>())
 })
+
+test('linkOptions', () => {
+  const defaultRouterLinkOptions = linkOptions<
+    { label: string },
+    DefaultRouter,
+    string,
+    '/'
+  >
+  const defaultRouterObjectsLinkOptions = linkOptions<
+    { label: string },
+    DefaultRouter,
+    string,
+    '/'
+  >
+
+  const routerAlwaysTrailingSlashLinkOptions = linkOptions<
+    { label: string },
+    RouterAlwaysTrailingSlashes,
+    string,
+    '/'
+  >
+
+  const routerNeverTrailingSlashLinkOptions = linkOptions<
+    { label: string },
+    RouterNeverTrailingSlashes,
+    string,
+    '/'
+  >
+  const routerPreserveTrailingSlashLinkOptions = linkOptions<
+    { label: string },
+    RouterPreserveTrailingSlashes,
+    string,
+    '/'
+  >
+
+  expectTypeOf(defaultRouterLinkOptions)
+    .parameter(0)
+    .toHaveProperty('to')
+    .toEqualTypeOf<
+      | '..'
+      | '.'
+      | '/'
+      | '/invoices'
+      | '/invoices/$invoiceId'
+      | '/invoices/$invoiceId/details/$detailId'
+      | '/invoices/$invoiceId/details'
+      | '/invoices/$invoiceId/details/$detailId/lines'
+      | '/invoices/$invoiceId/edit'
+      | '/posts'
+      | '/posts/$postId'
+      | undefined
+    >()
+
+  expectTypeOf(defaultRouterObjectsLinkOptions)
+    .parameter(0)
+    .toHaveProperty('to')
+    .toEqualTypeOf<
+      | '..'
+      | '.'
+      | '/'
+      | '/invoices'
+      | '/invoices/$invoiceId'
+      | '/invoices/$invoiceId/details/$detailId'
+      | '/invoices/$invoiceId/details/$detailId/lines'
+      | '/invoices/$invoiceId/details'
+      | '/invoices/$invoiceId/edit'
+      | '/posts'
+      | '/posts/$postId'
+      | undefined
+    >()
+
+  expectTypeOf(routerAlwaysTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('to')
+    .toEqualTypeOf<
+      | '../'
+      | './'
+      | '/'
+      | '/invoices/'
+      | '/invoices/$invoiceId/'
+      | '/invoices/$invoiceId/details/$detailId/'
+      | '/invoices/$invoiceId/details/$detailId/lines/'
+      | '/invoices/$invoiceId/details/'
+      | '/invoices/$invoiceId/edit/'
+      | '/posts/'
+      | '/posts/$postId/'
+      | undefined
+    >()
+
+  expectTypeOf(routerNeverTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('to')
+    .toEqualTypeOf<
+      | '..'
+      | '.'
+      | '/'
+      | '/invoices'
+      | '/invoices/$invoiceId'
+      | '/invoices/$invoiceId/details/$detailId'
+      | '/invoices/$invoiceId/details/$detailId/lines'
+      | '/invoices/$invoiceId/details'
+      | '/invoices/$invoiceId/edit'
+      | '/posts'
+      | '/posts/$postId'
+      | undefined
+    >()
+
+  expectTypeOf(routerPreserveTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('to')
+    .toEqualTypeOf<
+      | '..'
+      | '.'
+      | './'
+      | '../'
+      | '/'
+      | '/invoices'
+      | '/invoices/'
+      | '/invoices/$invoiceId'
+      | '/invoices/$invoiceId/'
+      | '/invoices/$invoiceId/details/$detailId'
+      | '/invoices/$invoiceId/details/$detailId/'
+      | '/invoices/$invoiceId/details/$detailId/lines'
+      | '/invoices/$invoiceId/details/$detailId/lines/'
+      | '/invoices/$invoiceId/details'
+      | '/invoices/$invoiceId/details/'
+      | '/invoices/$invoiceId/edit'
+      | '/invoices/$invoiceId/edit/'
+      | '/posts'
+      | '/posts/'
+      | '/posts/$postId'
+      | '/posts/$postId/'
+      | undefined
+    >()
+
+  expectTypeOf(defaultRouterLinkOptions)
+    .parameter(0)
+    .toMatchTypeOf<{ search: unknown }>()
+
+  expectTypeOf(defaultRouterObjectsLinkOptions)
+    .parameter(0)
+    .toMatchTypeOf<{ search: unknown }>()
+
+  expectTypeOf(routerAlwaysTrailingSlashLinkOptions)
+    .parameter(0)
+    .toMatchTypeOf<{ search: unknown }>()
+
+  expectTypeOf(routerNeverTrailingSlashLinkOptions)
+    .parameter(0)
+    .toMatchTypeOf<{ search: unknown }>()
+
+  expectTypeOf(routerPreserveTrailingSlashLinkOptions)
+    .parameter(0)
+    .toMatchTypeOf<{ search: unknown }>()
+
+  expectTypeOf(defaultRouterLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .exclude<Function | boolean>()
+    .toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(defaultRouterObjectsLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .exclude<Function | boolean>()
+    .toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(routerAlwaysTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .exclude<Function | boolean>()
+    .toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(routerNeverTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .exclude<Function | boolean>()
+    .toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(routerPreserveTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .exclude<Function | boolean>()
+    .toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(defaultRouterLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .parameter(0)
+    .toEqualTypeOf<{
+      page?: number
+      rootIndexPage?: number
+      rootPage?: number
+      linesPage?: number
+    }>()
+
+  expectTypeOf(defaultRouterObjectsLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .parameter(0)
+    .toEqualTypeOf<{
+      page?: number
+      rootIndexPage?: number
+      rootPage?: number
+      linesPage?: number
+    }>()
+
+  expectTypeOf(routerAlwaysTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .parameter(0)
+    .toEqualTypeOf<{
+      page?: number
+      rootIndexPage?: number
+      rootPage?: number
+      linesPage?: number
+    }>()
+
+  expectTypeOf(routerNeverTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .parameter(0)
+    .toEqualTypeOf<{
+      page?: number
+      rootIndexPage?: number
+      rootPage?: number
+      linesPage?: number
+    }>()
+
+  expectTypeOf(routerPreserveTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .parameter(0)
+    .toEqualTypeOf<{
+      page?: number
+      rootIndexPage?: number
+      rootPage?: number
+      linesPage?: number
+    }>()
+
+  expectTypeOf(defaultRouterLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .returns.toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(defaultRouterObjectsLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .returns.toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(routerAlwaysTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .returns.toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(routerNeverTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .returns.toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(routerPreserveTrailingSlashLinkOptions)
+    .parameter(0)
+    .toHaveProperty('search')
+    .returns.toEqualTypeOf<{ rootPage?: number; rootIndexPage: number }>()
+
+  expectTypeOf(defaultRouterLinkOptions).returns.toEqualTypeOf<{
+    label: string
+  }>()
+
+  expectTypeOf(defaultRouterObjectsLinkOptions).returns.toEqualTypeOf<{
+    label: string
+  }>()
+
+  expectTypeOf(routerAlwaysTrailingSlashLinkOptions).returns.toEqualTypeOf<{
+    label: string
+  }>()
+
+  expectTypeOf(routerNeverTrailingSlashLinkOptions).returns.toEqualTypeOf<{
+    label: string
+  }>()
+
+  expectTypeOf(routerPreserveTrailingSlashLinkOptions).returns.toEqualTypeOf<{
+    label: string
+  }>()
+})
diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx
index 88a225fd5..605448fc1 100644
--- a/packages/react-router/tests/link.test.tsx
+++ b/packages/react-router/tests/link.test.tsx
@@ -1095,6 +1095,37 @@ describe('Link', () => {
     expect(errorText).toBeInTheDocument()
   })
 
+  test('when navigating to the root with an error in component', async () => {
+    const notFoundComponent = vi.fn()
+
+    const rootRoute = createRootRoute({
+      errorComponent: () => <span>Expected rendering error message</span>,
+      notFoundComponent,
+    })
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      component: () => {
+        throw new Error(
+          'Error from component should not render notFoundComponent',
+        )
+      },
+    })
+
+    const router = createRouter({
+      routeTree: rootRoute.addChildren([indexRoute]),
+    })
+
+    render(<RouterProvider router={router} />)
+
+    const errorText = await screen.findByText(
+      'Expected rendering error message',
+    )
+    expect(errorText).toBeInTheDocument()
+    expect(notFoundComponent).not.toBeCalled()
+  })
+
   test('when navigating to /posts with params', async () => {
     const rootRoute = createRootRoute()
     const indexRoute = createRoute({
diff --git a/packages/react-router/tests/path.test.ts b/packages/react-router/tests/path.test.ts
index d77719c2a..0e9212524 100644
--- a/packages/react-router/tests/path.test.ts
+++ b/packages/react-router/tests/path.test.ts
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
 import {
   exactPathTest,
   interpolatePath,
+  matchPathname,
   removeBasepath,
   removeTrailingSlash,
   resolvePath,
@@ -276,3 +277,57 @@ describe('interpolatePath', () => {
     })
   })
 })
+
+describe('matchPathname', () => {
+  it.each([
+    {
+      name: 'should match the root path that start with the basepath',
+      basepath: '/basepath',
+      pathname: '/basepath',
+      matchLocation: {
+        to: '/',
+      },
+      expected: {},
+    },
+    {
+      name: 'should match the path that start with the basepath',
+      basepath: '/basepath',
+      pathname: '/basepath/abc',
+      matchLocation: {
+        to: '/abc',
+      },
+      expected: {},
+    },
+    {
+      name: 'should not match the root path that does not start with the basepath',
+      basepath: '/basepath',
+      pathname: '/',
+      matchLocation: {
+        to: '/',
+      },
+      expected: undefined,
+    },
+    {
+      name: 'should not match the path that does not start with the basepath',
+      basepath: '/basepath',
+      pathname: '/abc',
+      matchLocation: {
+        to: '/abc',
+      },
+      expected: undefined,
+    },
+    {
+      name: 'should not match the path that match partial of the basepath',
+      basepath: '/base',
+      pathname: '/basepath/abc',
+      matchLocation: {
+        to: '/abc',
+      },
+      expected: undefined,
+    },
+  ])('$name', ({ basepath, pathname, matchLocation, expected }) => {
+    expect(matchPathname(basepath, pathname, matchLocation)).toStrictEqual(
+      expected,
+    )
+  })
+})
diff --git a/packages/react-router/tests/route.test-d.tsx b/packages/react-router/tests/route.test-d.tsx
index 9becb5020..69286c8c0 100644
--- a/packages/react-router/tests/route.test-d.tsx
+++ b/packages/react-router/tests/route.test-d.tsx
@@ -38,7 +38,7 @@ test('when creating the root with routeContext', () => {
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: {}
-        search: {}
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
     },
@@ -109,7 +109,7 @@ test('when creating the root route with context and routeContext', () => {
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: { userId: string }
-        search: {}
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
     },
@@ -214,7 +214,7 @@ test('when creating the root route with context, routeContext, beforeLoad and a
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: { userId: string }
-        search: {}
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
 
@@ -325,7 +325,7 @@ test('when creating a child route with routeContext from the root route with con
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: { userId: string }
-        search: {}
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
 
@@ -622,7 +622,7 @@ test('when creating a child route with params, search with routeContext from the
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: { userId: string }
-        search: { page: number }
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
     },
@@ -670,7 +670,7 @@ test('when creating a child route with params, search with routeContext, beforeL
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: { userId: string }
-        search: { page: number }
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
       return {
@@ -786,7 +786,7 @@ test('when creating a child route with routeContext from a parent with routeCont
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: { userId: string }
-        search: {}
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
 
@@ -807,7 +807,7 @@ test('when creating a child route with routeContext from a parent with routeCont
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: { userId: string; invoiceId: string }
-        search: {}
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
 
@@ -915,7 +915,7 @@ test('when creating a child route with routeContext, beforeLoad, search, params,
         buildLocation: BuildLocationFn
         cause: 'preload' | 'enter' | 'stay'
         context: { userId: string }
-        search: { page: number }
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
       return { env: 'env1' }
@@ -960,7 +960,7 @@ test('when creating a child route with routeContext, beforeLoad, search, params,
           env: string
           invoicePermissions: readonly ['view']
         }
-        search: { page: number; detailPage: number }
+        deps: {}
         matches: Array<MakeRouteMatchUnion>
       }>()
       return { detailEnv: 'detailEnv' }
@@ -994,6 +994,27 @@ test('when creating a child route with routeContext, beforeLoad, search, params,
       detailPage: deps.search.detailPage,
       invoicePage: deps.search.page,
     }),
+    context: (opt) => {
+      expectTypeOf(opt).toEqualTypeOf<{
+        abortController: AbortController
+        preload: boolean
+        params: { invoiceId: string; detailId: string }
+        location: ParsedLocation
+        navigate: NavigateFn
+        buildLocation: BuildLocationFn
+        cause: 'preload' | 'enter' | 'stay'
+        context: {
+          userId: string
+          env: string
+          invoicePermissions: readonly ['view']
+          detailEnv: string
+          detailsPermissions: readonly ['view']
+        }
+        deps: { detailPage: number; invoicePage: number }
+        matches: Array<MakeRouteMatchUnion>
+      }>()
+      return { detailEnv: 'detailEnv' }
+    },
     loader: (opts) =>
       expectTypeOf(opts).toEqualTypeOf<{
         abortController: AbortController
diff --git a/packages/react-router/tests/routeContext.test.tsx b/packages/react-router/tests/routeContext.test.tsx
index ed2d06d47..a89eed07e 100644
--- a/packages/react-router/tests/routeContext.test.tsx
+++ b/packages/react-router/tests/routeContext.test.tsx
@@ -1,4 +1,5 @@
 import {
+  act,
   cleanup,
   configure,
   fireEvent,
@@ -6,6 +7,7 @@ import {
   screen,
 } from '@testing-library/react'
 import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
+import { z } from 'zod'
 
 import {
   Link,
@@ -40,6 +42,213 @@ const WAIT_TIME = 150
 describe('context function', () => {
   configure({ reactStrictMode: true })
 
+  describe('context is executed', () => {
+    async function findByText(text: string) {
+      const element = await screen.findByText(text)
+      expect(element).toBeInTheDocument()
+    }
+
+    async function clickButton(name: string) {
+      const button = await screen.findByRole('button', {
+        name,
+      })
+      expect(button).toBeInTheDocument()
+      fireEvent.click(button)
+    }
+
+    test('when the path params change', async () => {
+      const mockContextFn = 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: '/detail/$id', params: { id: 1 } })
+                }
+              >
+                detail-1
+              </button>
+            </div>
+          )
+        },
+      })
+      const detailRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        path: '/detail/$id',
+        params: {
+          parse: (p) => z.object({ id: z.coerce.number() }).parse(p),
+          stringify: (p) => ({ id: `${p.id}` }),
+        },
+        context: (args) => {
+          mockContextFn(args.params)
+        },
+        component: () => {
+          const id = detailRoute.useParams({ select: (params) => params.id })
+          const navigate = detailRoute.useNavigate()
+          return (
+            <div>
+              <h1>Detail page: {id}</h1>
+              <button
+                onClick={() =>
+                  navigate({
+                    to: '/detail/$id',
+                    params: (p: any) => ({ ...p, id: p.id + 1 }),
+                  })
+                }
+              >
+                next
+              </button>
+            </div>
+          )
+        },
+      })
+
+      const routeTree = rootRoute.addChildren([indexRoute, detailRoute])
+      const router = createRouter({ routeTree, history })
+
+      await act(() => render(<RouterProvider router={router} />))
+
+      await findByText('Index page')
+      expect(mockContextFn).not.toHaveBeenCalled()
+
+      await clickButton('detail-1')
+
+      await findByText('Detail page: 1')
+      console.log(mockContextFn.mock.calls)
+      expect(mockContextFn).toHaveBeenCalledOnce()
+      expect(mockContextFn).toHaveBeenCalledWith({ id: 1 })
+      mockContextFn.mockClear()
+      await clickButton('next')
+
+      await findByText('Detail page: 2')
+      expect(mockContextFn).toHaveBeenCalledOnce()
+      expect(mockContextFn).toHaveBeenCalledWith({ id: 2 })
+      mockContextFn.mockClear()
+      await clickButton('next')
+
+      await findByText('Detail page: 3')
+      expect(mockContextFn).toHaveBeenCalledOnce()
+      expect(mockContextFn).toHaveBeenCalledWith({ id: 3 })
+      mockContextFn.mockClear()
+    })
+
+    test('when loader deps change', async () => {
+      const mockContextFn = vi.fn()
+
+      const rootRoute = createRootRoute()
+      const indexRoute = createRoute({
+        getParentRoute: () => rootRoute,
+        validateSearch: z.object({
+          foo: z.string().optional(),
+          bar: z.string().optional(),
+        }),
+        path: '/',
+        loaderDeps: ({ search }) => ({ foo: search.foo }),
+        context: ({ deps }) => {
+          mockContextFn(deps)
+        },
+        component: () => {
+          const navigate = indexRoute.useNavigate()
+          return (
+            <div>
+              <h1>Index page</h1>
+              <h2>search: {JSON.stringify(indexRoute.useSearch())}</h2>
+              <button
+                onClick={() => {
+                  navigate({ search: (p: any) => ({ ...p, foo: 'foo-1' }) })
+                }}
+              >
+                foo-1
+              </button>
+              <button
+                onClick={() => {
+                  navigate({ search: (p: any) => ({ ...p, foo: 'foo-2' }) })
+                }}
+              >
+                foo-2
+              </button>
+              <button
+                onClick={() => {
+                  navigate({ search: (p: any) => ({ ...p, bar: 'bar-1' }) })
+                }}
+              >
+                bar-1
+              </button>
+              <button
+                onClick={() => {
+                  navigate({ search: (p: any) => ({ ...p, bar: 'bar-2' }) })
+                }}
+              >
+                bar-2
+              </button>
+              <button
+                onClick={() => {
+                  navigate({ search: {} })
+                }}
+              >
+                clear
+              </button>
+            </div>
+          )
+        },
+      })
+
+      const routeTree = rootRoute.addChildren([indexRoute])
+      const router = createRouter({ routeTree, history })
+
+      render(<RouterProvider router={router} />)
+
+      await findByText('Index page')
+      await findByText(`search: ${JSON.stringify({})}`)
+
+      expect(mockContextFn).toHaveBeenCalledOnce()
+      expect(mockContextFn).toHaveBeenCalledWith({})
+      mockContextFn.mockClear()
+
+      await clickButton('foo-1')
+      await findByText(`search: ${JSON.stringify({ foo: 'foo-1' })}`)
+      expect(mockContextFn).toHaveBeenCalledOnce()
+      expect(mockContextFn).toHaveBeenCalledWith({ foo: 'foo-1' })
+
+      mockContextFn.mockClear()
+      await clickButton('foo-1')
+      await findByText(`search: ${JSON.stringify({ foo: 'foo-1' })}`)
+      expect(mockContextFn).not.toHaveBeenCalled()
+
+      await clickButton('bar-1')
+      await findByText(
+        `search: ${JSON.stringify({ foo: 'foo-1', bar: 'bar-1' })}`,
+      )
+      expect(mockContextFn).not.toHaveBeenCalled()
+
+      await clickButton('foo-2')
+      await findByText(
+        `search: ${JSON.stringify({ foo: 'foo-2', bar: 'bar-1' })}`,
+      )
+      expect(mockContextFn).toHaveBeenCalledWith({ foo: 'foo-2' })
+      mockContextFn.mockClear()
+
+      await clickButton('bar-2')
+      await findByText(
+        `search: ${JSON.stringify({ foo: 'foo-2', bar: 'bar-2' })}`,
+      )
+      expect(mockContextFn).not.toHaveBeenCalled()
+
+      await clickButton('clear')
+      await findByText(`search: ${JSON.stringify({})}`)
+      expect(mockContextFn).toHaveBeenCalledOnce()
+      expect(mockContextFn).toHaveBeenCalledWith({})
+    })
+  })
+
   describe('accessing values in the context function', () => {
     test('receives an empty object', async () => {
       const mockIndexContextFn = vi.fn()