Repository: TanStack/router
Pull request #3368 (2025-02-08 21:28:12)

feat(react-router): add `shouldThrow` option to `useParams` and `useSearch`

nx-cloud[bot]: 
View your [CI Pipeline Execution ↗](https://cloud.nx.app/cipes/67a7cc7e66d43971aa352341?utm_source=pull-request&utm_medium=comment) for commit 2b5b771d1bd3844bec9972598f1e1142ab23dff7.

---

☁️ [Nx Cloud](https://cloud.nx.app?utm_source=pull-request&utm_medium=comment) last updated this comment at `2025-02-08 21:28:32` UTC

<!-- NX_CLOUD_APP_COMMENT_END -->
pkg-pr-new[bot]: 
[Open in Stackblitz](https://pkg.pr.new/template/7695b04b-9d6c-466a-bc66-78b9328da9e7)
<details><summary><b>More templates</b></summary><p>

- [tanstack-router-react-example-authenticated-routes](https://pkg.pr.new/template/1b3d93b5-a02d-437f-921d-6a52917158be)
- [tanstack-router-react-example-basic-default-search-params](https://pkg.pr.new/template/9f8d4c12-7518-4c5c-8881-ac71a9e16219)
- [tanstack-router-react-example-basic-file-based](https://pkg.pr.new/template/be7eaee9-115c-49c3-8700-91935a3c4d42)
- [tanstack-router-react-example-basic](https://pkg.pr.new/template/16d5562a-d9cb-4b88-9d5e-4e1d0dde93e7)
- [tanstack-router-react-example-basic-file-based-codesplitting](https://pkg.pr.new/template/7d1533a1-e324-4b13-9f0c-c309e97ff10f)
- [tanstack-router-react-example-react-query](https://pkg.pr.new/template/55d22069-d72f-45f4-b04f-7e20fb3c5c7a)
- [tanstack-router-react-example-basic-react-query-file-based](https://pkg.pr.new/template/51042d71-62c5-4600-bffe-c945c1f3f971)
- [tanstack-router-react-example-basic-ssr-file-based](https://pkg.pr.new/template/83d4d9c2-d1a7-4f7c-a1c0-9d1ad419dfec)
- [tanstack-router-react-example-basic-ssr-streaming-file-based](https://pkg.pr.new/template/4b8eae4d-f454-4155-a8cc-87ce64ba7333)
- [tanstack-router-react-example-deferred-data](https://pkg.pr.new/template/9a1d7b32-30b1-4c0a-a697-8d5dced14038)
- [tanstack-router-react-example-basic-virtual-inside-file-based](https://pkg.pr.new/template/d464a951-1828-4686-b93e-2aab1fde5af8)
- [tanstack-router-react-example-basic-virtual-file-based](https://pkg.pr.new/template/b84f7b36-1881-434c-950b-edc0ece6d653)
- [tanstack-router-react-example-kitchen-sink](https://pkg.pr.new/template/6b0e29c4-9965-4a11-9af5-967f13d025d5)
- [tanstack-router-react-example-kitchen-sink-file-based](https://pkg.pr.new/template/d76f832f-08e6-4936-8f14-8110ff5a2722)
- [tanstack-router-react-example-kitchen-sink-react-query](https://pkg.pr.new/template/e48070de-cc37-46fa-8851-0bbf5f147761)
- [tanstack-router-react-example-kitchen-sink-react-query-file-based](https://pkg.pr.new/template/34dcd4c7-76f4-4411-a77b-3833416df93a)
- [tanstack-router-react-example-large-file-based](https://pkg.pr.new/template/f1b814ce-7458-4a4f-a38e-9120b1b154be)
- [tanstack-router-react-example-location-masking](https://pkg.pr.new/template/5aa8aba7-3ff4-4848-806c-fd8556b60a77)
- [tanstack-router-react-example-quickstart](https://pkg.pr.new/template/b00984c0-084a-4169-acf9-a913e32b524b)
- [tanstack-router-react-example-navigation-blocking](https://pkg.pr.new/template/581743a1-fefb-4b16-9ed2-7bab7fe9dbde)
- [tanstack-router-react-example-quickstart-esbuild-file-based](https://pkg.pr.new/template/1d383736-88ae-475c-86ac-d9cd93541289)
- [tanstack-router-react-example-quickstart-rspack-file-based](https://pkg.pr.new/template/7e6603e7-e5c4-46ac-8eff-7e5539505965)
- [tanstack-router-react-example-quickstart-file-based](https://pkg.pr.new/template/c0cab189-e765-45a5-bd75-f9e9342d7207)
- [tanstack-router-react-example-quickstart-webpack-file-based](https://pkg.pr.new/template/f5124c2b-b948-474e-bf7d-0274ee66a7ff)
- [router-monorepo-react-query](https://pkg.pr.new/template/71b7798e-4f08-458b-85a4-fbcd203be799)
- [router-mono-simple](https://pkg.pr.new/template/9b5a19dd-9c8c-439f-aca4-3640d866e327)
- [router-mono-simple-lazy](https://pkg.pr.new/template/b7312e25-be8a-4af3-8dc6-4978e43b1273)
- [tanstack-router-react-example-scroll-restoration](https://pkg.pr.new/template/3893f251-5d40-42b1-8850-a69f21c1c1f2)
- [tanstack-search-validator-adapters](https://pkg.pr.new/template/c082e52e-81d7-4096-8edf-726e9a6ebb31)
- [tanstack-start-example-basic](https://pkg.pr.new/template/10394774-39f9-41ab-b5ec-bcc78f68907e)
- [tanstack-start-example-basic-react-query](https://pkg.pr.new/template/d9d2ebac-8ec4-476e-b156-7fd6a47fc02c)
- [tanstack-start-example-basic-auth](https://pkg.pr.new/template/65348830-a8de-4f51-b6de-8c46ea5047f6)
- [tanstack-start-example-basic-rsc](https://pkg.pr.new/template/15bbf715-f8da-42b7-b16b-3fd14dd2bdce)
- [tanstack-start-example-clerk-basic](https://pkg.pr.new/template/a68c5805-348d-433d-a6d0-7d39b2352c79)
- [tanstack-start-example-convex-trellaux](https://pkg.pr.new/template/afb445db-e68e-46e8-8e2f-ae67c53a411c)
- [tanstack-start-example-counter](https://pkg.pr.new/template/f60168f0-9e47-482b-ab1c-85fbe4ad2cf4)
- [tanstack-start-example-large](https://pkg.pr.new/template/488487f4-a9f0-4961-8d6e-bc8e0708f185)
- [tanstack-start-example-supabase-basic](https://pkg.pr.new/template/9d781472-a1ba-4811-9970-b6b4d3e8da4b)
- [tanstack-start-example-trellaux](https://pkg.pr.new/template/ec524706-4504-42d5-983d-42ebd8a89009)
- [tanstack-router-react-example-with-framer-motion](https://pkg.pr.new/template/1b6766c1-d565-4b33-a0f0-2133040b196a)
- [tanstack-router-react-example-with-trpc](https://pkg.pr.new/template/1eae384d-ebf6-4874-b0ac-977319a8858f)
- [tanstack-router-react-example-with-trpc-react-query](https://pkg.pr.new/template/805f7284-1dc3-4543-99be-a7bd366e5de6)

</p></details>
      
    


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

```
npm i https://pkg.pr.new/@tanstack/arktype-adapter@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/create-router@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/directive-functions-plugin</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/directive-functions-plugin@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/eslint-plugin-router@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/create-start@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/history@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/react-cross-context@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/react-router@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/react-router-with-query@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/router-cli@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/router-core@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/router-devtools@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/router-generator@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/router-plugin@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/router-utils@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/router-vite-plugin@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/server-functions-plugin</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/server-functions-plugin@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/start@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/start-api-routes</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/start-api-routes@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/start-client@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/start-config@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/start-plugin@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/start-router-manifest</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/start-router-manifest@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/start-server@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/start-server-functions-client</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/start-server-functions-client@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/start-server-functions-fetcher</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/start-server-functions-fetcher@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/start-server-functions-handler</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/start-server-functions-handler@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/start-server-functions-server</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/start-server-functions-server@3368
```

</p></details>
      
    

<details><summary><b>@tanstack/start-server-functions-ssr</b></summary><p>

```
npm i https://pkg.pr.new/@tanstack/start-server-functions-ssr@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/valibot-adapter@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/virtual-file-routes@3368
```

</p></details>
      
    

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

```
npm i https://pkg.pr.new/@tanstack/zod-adapter@3368
```

</p></details>
      
    

_commit: <a href="https://github.com/TanStack/router/runs/36902680496"><code>2b5b771</code></a>_


Patch:
diff --git a/docs/router/framework/react/api/router/useParamsHook.md b/docs/router/framework/react/api/router/useParamsHook.md
index d5068325c..0f3ca88fc 100644
--- a/docs/router/framework/react/api/router/useParamsHook.md
+++ b/docs/router/framework/react/api/router/useParamsHook.md
@@ -15,6 +15,13 @@ The `useParams` hook accepts an optional `options` object.
 - Optional - `default: true`
 - If `false`, the `opts.from` option will be ignored and types will be loosened to `Partial<AllParams>` to reflect the shared types of all params.
 
+### `opts.shouldThrow` option
+
+- Type: `boolean`
+- Optional
+- `default: true`
+- If `false`,`useParams` will not throw an invariant exception in case a match was not found in the currently rendered matches; in this case, it will return `undefined`.
+
 ### `opts.select` option
 
 - Optional
diff --git a/docs/router/framework/react/api/router/useSearchHook.md b/docs/router/framework/react/api/router/useSearchHook.md
index e911d8d13..39c8ed5d9 100644
--- a/docs/router/framework/react/api/router/useSearchHook.md
+++ b/docs/router/framework/react/api/router/useSearchHook.md
@@ -15,6 +15,13 @@ The `useSearch` hook accepts an `options` object.
 - Required
 - The RouteID to match the search query parameters from.
 
+### `opts.shouldThrow` option
+
+- Type: `boolean`
+- Optional
+- `default: true`
+- If `false`,`useSearch` will not throw an invariant exception in case a match was not found in the currently rendered matches; in this case, it will return `undefined`.
+
 ### `opts.select` option
 
 - Type: `(search: SelectedSearchSchema) => TSelected`
diff --git a/examples/react/start-large/app/typePrimitives.tsx b/examples/react/start-large/app/typePrimitives.tsx
index dc71364b7..bac50f42d 100644
--- a/examples/react/start-large/app/typePrimitives.tsx
+++ b/examples/react/start-large/app/typePrimitives.tsx
@@ -67,7 +67,7 @@ export const useCustomId = <TId extends string>(id: ValidateId<TId>) => {
 export const useCustomSearch = <TOptions,>(
   options: ValidateUseSearchOptions<TOptions>,
 ): ValidateUseSearchResult<TOptions> => {
-  return useSearch(options as never)
+  return useSearch(options as never) as any
 }
 
 export const useCustomParams = <TOptions,>(
diff --git a/packages/react-router/src/fileRoute.ts b/packages/react-router/src/fileRoute.ts
index 242ea2766..015856fa8 100644
--- a/packages/react-router/src/fileRoute.ts
+++ b/packages/react-router/src/fileRoute.ts
@@ -233,19 +233,21 @@ export class LazyRoute<TRoute extends AnyRoute> {
   }
 
   useSearch: UseSearchRoute<TRoute['id']> = (opts) => {
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
     return useSearch({
       select: opts?.select,
       structuralSharing: opts?.structuralSharing,
       from: this.options.id,
-    } as any)
+    } as any) as any
   }
 
   useParams: UseParamsRoute<TRoute['id']> = (opts) => {
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
     return useParams({
       select: opts?.select,
       structuralSharing: opts?.structuralSharing,
       from: this.options.id,
-    } as any)
+    } as any) as any
   }
 
   useLoaderDeps: UseLoaderDepsRoute<TRoute['id']> = (opts) => {
diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts
index 5ed7040e3..4f23cd8a8 100644
--- a/packages/react-router/src/route.ts
+++ b/packages/react-router/src/route.ts
@@ -648,19 +648,21 @@ export class RouteApi<TId, TRouter extends AnyRouter = RegisteredRouter> {
   }
 
   useSearch: UseSearchRoute<TId> = (opts) => {
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
     return useSearch({
       select: opts?.select,
       structuralSharing: opts?.structuralSharing,
       from: this.id,
-    } as any)
+    } as any) as any
   }
 
   useParams: UseParamsRoute<TId> = (opts) => {
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
     return useParams({
       select: opts?.select,
       structuralSharing: opts?.structuralSharing,
       from: this.id,
-    } as any)
+    } as any) as any
   }
 
   useLoaderDeps: UseLoaderDepsRoute<TId> = (opts) => {
@@ -1056,19 +1058,21 @@ export class Route<
   }
 
   useSearch: UseSearchRoute<TId> = (opts) => {
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
     return useSearch({
       select: opts?.select,
       structuralSharing: opts?.structuralSharing,
       from: this.id,
-    } as any)
+    } as any) as any
   }
 
   useParams: UseParamsRoute<TId> = (opts) => {
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
     return useParams({
       select: opts?.select,
       structuralSharing: opts?.structuralSharing,
       from: this.id,
-    } as any)
+    } as any) as any
   }
 
   useLoaderDeps: UseLoaderDepsRoute<TId> = (opts) => {
diff --git a/packages/react-router/src/typePrimitives.ts b/packages/react-router/src/typePrimitives.ts
index c71e06991..558093f4b 100644
--- a/packages/react-router/src/typePrimitives.ts
+++ b/packages/react-router/src/typePrimitives.ts
@@ -125,6 +125,15 @@ export type InferStrict<TOptions> = TOptions extends {
   ? TStrict
   : true
 
+/**
+ * @internal
+ */
+export type InferShouldThrow<TOptions> = TOptions extends {
+  shouldThrow: infer TShouldThrow extends boolean
+}
+  ? TShouldThrow
+  : true
+
 /**
  * @internal
  */
@@ -152,6 +161,7 @@ export type ValidateUseSearchOptions<
     TRouter,
     InferFrom<TOptions>,
     InferStrict<TOptions>,
+    InferShouldThrow<TOptions>,
     InferSelected<TOptions>,
     InferStructuralSharing<TOptions>
   >
@@ -176,6 +186,7 @@ export type ValidateUseParamsOptions<
     TRouter,
     InferFrom<TOptions>,
     InferStrict<TOptions>,
+    InferShouldThrow<TOptions>,
     InferSelected<TOptions>,
     InferSelected<TOptions>
   >
diff --git a/packages/react-router/src/useMatch.tsx b/packages/react-router/src/useMatch.tsx
index 51cf0bf7d..351d29a2f 100644
--- a/packages/react-router/src/useMatch.tsx
+++ b/packages/react-router/src/useMatch.tsx
@@ -15,7 +15,7 @@ export interface UseMatchBaseOptions<
   TRouter extends AnyRouter,
   TFrom,
   TStrict extends boolean,
-  TThrow,
+  TThrow extends boolean,
   TSelected,
   TStructuralSharing extends boolean,
 > {
@@ -45,8 +45,8 @@ export type UseMatchOptions<
   TRouter extends AnyRouter,
   TFrom extends string | undefined,
   TStrict extends boolean,
-  TSelected,
   TThrow extends boolean,
+  TSelected,
   TStructuralSharing extends boolean,
 > = StrictOrFrom<TRouter, TFrom, TStrict> &
   UseMatchBaseOptions<
@@ -70,7 +70,7 @@ export type UseMatchResult<
     : MakeRouteMatchUnion<TRouter>
   : TSelected
 
-type ThrowConstraint<
+export type ThrowConstraint<
   TStrict extends boolean,
   TThrow extends boolean,
 > = TStrict extends false ? (TThrow extends true ? never : TThrow) : TThrow
@@ -87,8 +87,8 @@ export function useMatch<
     TRouter,
     TFrom,
     TStrict,
-    TSelected,
     ThrowConstraint<TStrict, TThrow>,
+    TSelected,
     TStructuralSharing
   >,
 ): ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow> {
diff --git a/packages/react-router/src/useParams.tsx b/packages/react-router/src/useParams.tsx
index c096075cc..25f99e2ef 100644
--- a/packages/react-router/src/useParams.tsx
+++ b/packages/react-router/src/useParams.tsx
@@ -1,4 +1,5 @@
 import { useMatch } from './useMatch'
+import type { ThrowConstraint } from './useMatch'
 import type {
   StructuralSharingOption,
   ValidateSelected,
@@ -6,28 +7,38 @@ import type {
 import type { AllParams, RouteById } from './routeInfo'
 import type { AnyRouter, RegisteredRouter } from './router'
 import type { StrictOrFrom } from './utils'
-import type { Expand } from '@tanstack/router-core'
+import type { Expand, ThrowOrOptional } from '@tanstack/router-core'
 
 export interface UseParamsBaseOptions<
   TRouter extends AnyRouter,
   TFrom,
   TStrict extends boolean,
+  TThrow extends boolean,
   TSelected,
   TStructuralSharing,
 > {
   select?: (
     params: ResolveParams<TRouter, TFrom, TStrict>,
   ) => ValidateSelected<TRouter, TSelected, TStructuralSharing>
+  shouldThrow?: TThrow
 }
 
 export type UseParamsOptions<
   TRouter extends AnyRouter,
   TFrom extends string | undefined,
   TStrict extends boolean,
+  TThrow extends boolean,
   TSelected,
   TStructuralSharing,
 > = StrictOrFrom<TRouter, TFrom, TStrict> &
-  UseParamsBaseOptions<TRouter, TFrom, TStrict, TSelected, TStructuralSharing> &
+  UseParamsBaseOptions<
+    TRouter,
+    TFrom,
+    TStrict,
+    TThrow,
+    TSelected,
+    TStructuralSharing
+  > &
   StructuralSharingOption<TRouter, TSelected, TStructuralSharing>
 
 export type ResolveParams<
@@ -55,7 +66,8 @@ export type UseParamsRoute<out TFrom> = <
   opts?: UseParamsBaseOptions<
     TRouter,
     TFrom,
-    true,
+    /* TStrict */ true,
+    /* TThrow */ true,
     TSelected,
     TStructuralSharing
   > &
@@ -66,6 +78,7 @@ export function useParams<
   TRouter extends AnyRouter = RegisteredRouter,
   const TFrom extends string | undefined = undefined,
   TStrict extends boolean = true,
+  TThrow extends boolean = true,
   TSelected = unknown,
   TStructuralSharing extends boolean = boolean,
 >(
@@ -73,16 +86,21 @@ export function useParams<
     TRouter,
     TFrom,
     TStrict,
+    ThrowConstraint<TStrict, TThrow>,
     TSelected,
     TStructuralSharing
   >,
-): UseParamsResult<TRouter, TFrom, TStrict, TSelected> {
+): ThrowOrOptional<
+  UseParamsResult<TRouter, TFrom, TStrict, TSelected>,
+  TThrow
+> {
   return useMatch({
     from: opts.from!,
     strict: opts.strict,
+    shouldThrow: opts.shouldThrow,
     structuralSharing: opts.structuralSharing,
     select: (match: any) => {
       return opts.select ? opts.select(match.params) : match.params
     },
-  } as any) as UseParamsResult<TRouter, TFrom, TStrict, TSelected>
+  }) as any
 }
diff --git a/packages/react-router/src/useSearch.tsx b/packages/react-router/src/useSearch.tsx
index bb92db142..eb3aaae4c 100644
--- a/packages/react-router/src/useSearch.tsx
+++ b/packages/react-router/src/useSearch.tsx
@@ -1,4 +1,5 @@
 import { useMatch } from './useMatch'
+import type { ThrowConstraint } from './useMatch'
 import type {
   StructuralSharingOption,
   ValidateSelected,
@@ -6,28 +7,38 @@ import type {
 import type { FullSearchSchema, RouteById } from './routeInfo'
 import type { AnyRouter, RegisteredRouter } from './router'
 import type { StrictOrFrom } from './utils'
-import type { Expand } from '@tanstack/router-core'
+import type { Expand, ThrowOrOptional } from '@tanstack/router-core'
 
 export interface UseSearchBaseOptions<
   TRouter extends AnyRouter,
   TFrom,
   TStrict extends boolean,
+  TThrow extends boolean,
   TSelected,
   TStructuralSharing,
 > {
   select?: (
     state: ResolveSearch<TRouter, TFrom, TStrict>,
   ) => ValidateSelected<TRouter, TSelected, TStructuralSharing>
+  shouldThrow?: TThrow
 }
 
 export type UseSearchOptions<
   TRouter extends AnyRouter,
   TFrom,
   TStrict extends boolean,
+  TThrow extends boolean,
   TSelected,
   TStructuralSharing,
 > = StrictOrFrom<TRouter, TFrom, TStrict> &
-  UseSearchBaseOptions<TRouter, TFrom, TStrict, TSelected, TStructuralSharing> &
+  UseSearchBaseOptions<
+    TRouter,
+    TFrom,
+    TStrict,
+    TThrow,
+    TSelected,
+    TStructuralSharing
+  > &
   StructuralSharingOption<TRouter, TSelected, TStructuralSharing>
 
 export type UseSearchResult<
@@ -55,7 +66,8 @@ export type UseSearchRoute<out TFrom> = <
   opts?: UseSearchBaseOptions<
     TRouter,
     TFrom,
-    true,
+    /* TStrict */ true,
+    /* TThrow */ true,
     TSelected,
     TStructuralSharing
   > &
@@ -66,6 +78,7 @@ export function useSearch<
   TRouter extends AnyRouter = RegisteredRouter,
   const TFrom extends string | undefined = undefined,
   TStrict extends boolean = true,
+  TThrow extends boolean = true,
   TSelected = unknown,
   TStructuralSharing extends boolean = boolean,
 >(
@@ -73,16 +86,21 @@ export function useSearch<
     TRouter,
     TFrom,
     TStrict,
+    ThrowConstraint<TStrict, TThrow>,
     TSelected,
     TStructuralSharing
   >,
-): UseSearchResult<TRouter, TFrom, TStrict, TSelected> {
+): ThrowOrOptional<
+  UseSearchResult<TRouter, TFrom, TStrict, TSelected>,
+  TThrow
+> {
   return useMatch({
     from: opts.from!,
     strict: opts.strict,
+    shouldThrow: opts.shouldThrow,
     structuralSharing: opts.structuralSharing,
     select: (match: any) => {
       return opts.select ? opts.select(match.search) : match.search
     },
-  }) as UseSearchResult<TRouter, TFrom, TStrict, TSelected>
+  }) as any
 }


Test patch:
diff --git a/packages/react-router/tests/useParams.test-d.tsx b/packages/react-router/tests/useParams.test-d.tsx
index 4b4f13162..d243b05ce 100644
--- a/packages/react-router/tests/useParams.test-d.tsx
+++ b/packages/react-router/tests/useParams.test-d.tsx
@@ -1,274 +1,366 @@
-import { expectTypeOf, test } from 'vitest'
+import { describe, expectTypeOf, test } from 'vitest'
 import { createRootRoute, createRoute, createRouter, useParams } from '../src'
 
-test('when there are no params', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useParams<DefaultRouter>)
-    .parameter(0)
-    .toHaveProperty('from')
-    .toEqualTypeOf<'/invoices' | '__root__' | '/invoices/' | '/'>()
-
-  expectTypeOf(useParams<DefaultRouter>)
-    .parameter(0)
-    .toHaveProperty('strict')
-    .toEqualTypeOf<true | undefined>()
-
-  expectTypeOf(useParams<DefaultRouter, '/'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .parameter(0)
-    .toEqualTypeOf<{}>()
-
-  expectTypeOf(useParams<DefaultRouter, '/'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .returns.toEqualTypeOf<unknown>()
-
-  expectTypeOf(useParams<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/', false>({
-      strict: false,
-    }),
-  ).toEqualTypeOf<{}>()
-})
-
-test('when there is one param', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useParams<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices/$invoiceId'>,
-  ).returns.toEqualTypeOf<{ invoiceId: string }>()
-
-  expectTypeOf(useParams<DefaultRouter, '/invoices/$invoiceId'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { invoiceId: string }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices', false>,
-  ).returns.toEqualTypeOf<{ invoiceId?: string }>()
-
-  expectTypeOf(useParams<DefaultRouter, '/invoices', false>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { invoiceId?: string }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices', false, number>,
-  ).returns.toEqualTypeOf<number>()
-})
-
-test('when there are multiple params', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
+describe('useParams', () => {
+  test('when there are no params', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useParams<DefaultRouter>)
+      .parameter(0)
+      .toHaveProperty('from')
+      .toEqualTypeOf<'/invoices' | '__root__' | '/invoices/' | '/'>()
+
+    expectTypeOf(useParams<DefaultRouter>)
+      .parameter(0)
+      .toHaveProperty('strict')
+      .toEqualTypeOf<true | undefined>()
+
+    expectTypeOf(useParams<DefaultRouter, '/'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .parameter(0)
+      .toEqualTypeOf<{}>()
+
+    expectTypeOf(useParams<DefaultRouter, '/'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .returns.toEqualTypeOf<unknown>()
+
+    expectTypeOf(useParams<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(
+      useParams<DefaultRouter, '/', false>({
+        strict: false,
+      }),
+    ).toEqualTypeOf<{}>()
   })
 
-  const postsRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'posts',
+  test('when there is one param', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useParams<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(
+      useParams<DefaultRouter, '/invoices/$invoiceId'>,
+    ).returns.toEqualTypeOf<{ invoiceId: string }>()
+
+    expectTypeOf(useParams<DefaultRouter, '/invoices/$invoiceId'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { invoiceId: string }) => unknown) | undefined>()
+
+    expectTypeOf(
+      useParams<DefaultRouter, '/invoices', false>,
+    ).returns.toEqualTypeOf<{ invoiceId?: string }>()
+
+    expectTypeOf(useParams<DefaultRouter, '/invoices', false>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        ((search: { invoiceId?: string }) => unknown) | undefined
+      >()
+
+    expectTypeOf(
+      useParams<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        number
+      >,
+    ).returns.toEqualTypeOf<number>()
   })
 
-  const postRoute = createRoute({
-    getParentRoute: () => postsRoute,
-    path: '$postId',
+  test('when there are multiple params', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+    })
+
+    const postsRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'posts',
+    })
+
+    const postRoute = createRoute({
+      getParentRoute: () => postsRoute,
+      path: '$postId',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([
+        invoicesIndexRoute,
+        invoiceRoute,
+        postsRoute.addChildren([postRoute]),
+      ]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useParams<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(
+      useParams<DefaultRouter, '/invoices/$invoiceId'>,
+    ).returns.toEqualTypeOf<{ invoiceId: string }>()
+
+    expectTypeOf(useParams<DefaultRouter, '/invoices/$invoiceId'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { invoiceId: string }) => unknown) | undefined>()
+
+    expectTypeOf(
+      useParams<DefaultRouter, '/invoices', false>,
+    ).returns.toEqualTypeOf<{ invoiceId?: string; postId?: string }>()
+
+    expectTypeOf(useParams<DefaultRouter, '/invoices', false>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: { invoiceId?: string; postId?: string }) => unknown)
+        | undefined
+      >()
+
+    expectTypeOf(
+      useParams<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ true,
+        /* shouldThrow */ true,
+        { func: () => void }
+      >,
+    )
+      .parameter(0)
+      .exclude<undefined>()
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: {}) => {
+            func: () => void
+          })
+        | undefined
+      >()
+
+    expectTypeOf(
+      useParams<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ true,
+        /* shouldThrow */ true,
+        { func: () => void }
+      >,
+    )
+      .parameter(0)
+      .exclude<undefined>()
+      .toHaveProperty('structuralSharing')
+      .toEqualTypeOf<false | undefined>()
+
+    expectTypeOf(
+      useParams<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ true,
+        /* shouldThrow */ true,
+        { func: () => void },
+        true
+      >,
+    )
+      .parameter(0)
+      .exclude<undefined>()
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: {}) => {
+            func: 'Function is not serializable'
+          })
+        | undefined
+      >()
+
+    expectTypeOf(
+      useParams<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ true,
+        /* shouldThrow */ true,
+        { func: () => void },
+        true
+      >,
+    )
+      .parameter(0)
+      .exclude<undefined>()
+      .toHaveProperty('structuralSharing')
+      .toEqualTypeOf<false | undefined>()
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const routerWithStructuralSharing = createRouter({
+      routeTree,
+      defaultStructuralSharing: true,
+    })
+
+    expectTypeOf(
+      useParams<
+        typeof routerWithStructuralSharing,
+        '/invoices',
+        /* strict */ true,
+        /* shouldThrow */ true,
+        { func: () => void },
+        true
+      >,
+    )
+      .parameter(0)
+      .exclude<undefined>()
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: {}) => {
+            func: 'Function is not serializable'
+          })
+        | undefined
+      >()
+
+    expectTypeOf(
+      useParams<
+        typeof routerWithStructuralSharing,
+        '/invoices',
+        /* strict */ true,
+        /* shouldThrow */ true,
+        { func: () => void },
+        true
+      >,
+    )
+      .parameter(0)
+      .exclude<undefined>()
+      .toHaveProperty('structuralSharing')
+      .toEqualTypeOf<false>()
   })
 
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([
-      invoicesIndexRoute,
-      invoiceRoute,
-      postsRoute.addChildren([postRoute]),
-    ]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
+  describe('shouldThrow', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+      validateSearch: () => ({ page: 0 }),
+    })
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$id',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoiceRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const router = createRouter({ routeTree })
+
+    const from = '/invoices/$id'
+    test('return type is `{ id: string }` when shouldThrow = true', () => {
+      const shouldThrow = true
+      const params = useParams<
+        typeof router,
+        typeof from,
+        /* strict */ true,
+        typeof shouldThrow
+      >({ from, shouldThrow })
+
+      expectTypeOf(params).toEqualTypeOf<{ id: string }>()
+    })
+
+    test('return type is `{page: string} | undefined` when shouldThrow = false', () => {
+      const shouldThrow = false
+      const params = useParams<
+        typeof router,
+        typeof from,
+        /* strict */ true,
+        typeof shouldThrow
+      >({ from, shouldThrow })
+
+      expectTypeOf(params).toEqualTypeOf<{ id: string } | undefined>()
+    })
   })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useParams<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices/$invoiceId'>,
-  ).returns.toEqualTypeOf<{ invoiceId: string }>()
-
-  expectTypeOf(useParams<DefaultRouter, '/invoices/$invoiceId'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { invoiceId: string }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices', false>,
-  ).returns.toEqualTypeOf<{ invoiceId?: string; postId?: string }>()
-
-  expectTypeOf(useParams<DefaultRouter, '/invoices', false>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      ((search: { invoiceId?: string; postId?: string }) => unknown) | undefined
-    >()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices', true, { func: () => void }>,
-  )
-    .parameter(0)
-    .exclude<undefined>()
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: {}) => {
-          func: () => void
-        })
-      | undefined
-    >()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices', true, { func: () => void }>,
-  )
-    .parameter(0)
-    .exclude<undefined>()
-    .toHaveProperty('structuralSharing')
-    .toEqualTypeOf<false | undefined>()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices', true, { func: () => void }, true>,
-  )
-    .parameter(0)
-    .exclude<undefined>()
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: {}) => {
-          func: 'Function is not serializable'
-        })
-      | undefined
-    >()
-
-  expectTypeOf(
-    useParams<DefaultRouter, '/invoices', true, { func: () => void }, true>,
-  )
-    .parameter(0)
-    .exclude<undefined>()
-    .toHaveProperty('structuralSharing')
-    .toEqualTypeOf<false | undefined>()
-
-  const routerWithStructuralSharing = createRouter({
-    routeTree,
-    defaultStructuralSharing: true,
-  })
-
-  expectTypeOf(
-    useParams<
-      typeof routerWithStructuralSharing,
-      '/invoices',
-      true,
-      { func: () => void },
-      true
-    >,
-  )
-    .parameter(0)
-    .exclude<undefined>()
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: {}) => {
-          func: 'Function is not serializable'
-        })
-      | undefined
-    >()
-
-  expectTypeOf(
-    useParams<
-      typeof routerWithStructuralSharing,
-      '/invoices',
-      true,
-      { func: () => void },
-      true
-    >,
-  )
-    .parameter(0)
-    .exclude<undefined>()
-    .toHaveProperty('structuralSharing')
-    .toEqualTypeOf<false>()
 })
diff --git a/packages/react-router/tests/useSearch.test-d.tsx b/packages/react-router/tests/useSearch.test-d.tsx
index 7908b9aac..cf48f6575 100644
--- a/packages/react-router/tests/useSearch.test-d.tsx
+++ b/packages/react-router/tests/useSearch.test-d.tsx
@@ -1,610 +1,715 @@
-import { expectTypeOf, test } from 'vitest'
+import { describe, expectTypeOf, test } from 'vitest'
 import { createRootRoute, createRoute, createRouter, useSearch } from '../src'
 import type { SearchSchemaInput } from '../src'
 
-test('when there are no search params', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useSearch<DefaultRouter>)
-    .parameter(0)
-    .toHaveProperty('from')
-    .toEqualTypeOf<
-      '/invoices' | '__root__' | '/invoices/$invoiceId' | '/invoices/' | '/'
-    >()
-
-  expectTypeOf(useSearch<DefaultRouter>)
-    .parameter(0)
-    .toHaveProperty('strict')
-    .toEqualTypeOf<true | undefined>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .parameter(0)
-    .toEqualTypeOf<{}>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .returns.toEqualTypeOf<unknown>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/', false>({
-      strict: false,
-    }),
-  ).toEqualTypeOf<{}>()
-})
-
-test('when there is one search params', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-    validateSearch: () => ({ page: 0 }),
+describe('useSearch', () => {
+  test('when there are no search params', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useSearch<DefaultRouter>)
+      .parameter(0)
+      .toHaveProperty('from')
+      .toEqualTypeOf<
+        '/invoices' | '__root__' | '/invoices/$invoiceId' | '/invoices/' | '/'
+      >()
+
+    expectTypeOf(useSearch<DefaultRouter>)
+      .parameter(0)
+      .toHaveProperty('strict')
+      .toEqualTypeOf<true | undefined>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .parameter(0)
+      .toEqualTypeOf<{}>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .returns.toEqualTypeOf<unknown>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '/', false>({
+        strict: false,
+      }),
+    ).toEqualTypeOf<{}>()
+  })
+
+  test('when there is one search params', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+      validateSearch: () => ({ page: 0 }),
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
+      page: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { page: number }) => unknown) | undefined>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '/invoices', false>,
+    ).returns.toEqualTypeOf<{ page?: number }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { page?: number }) => unknown) | undefined>()
+
+    expectTypeOf(
+      useSearch<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        number
+      >,
+    ).returns.toEqualTypeOf<number>()
+
+    expectTypeOf(
+      useSearch<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { func: () => void }
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        ((search: { page?: number }) => { func: () => void }) | undefined
+      >()
+
+    expectTypeOf(
+      useSearch<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { func: () => void }
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('structuralSharing')
+      .toEqualTypeOf<false | undefined>()
+
+    expectTypeOf(
+      useSearch<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { func: () => void },
+        true
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: { page?: number }) => {
+            func: 'Function is not serializable'
+          })
+        | undefined
+      >()
+
+    expectTypeOf(
+      useSearch<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { func: () => void },
+        true
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('structuralSharing')
+      .toEqualTypeOf<false | undefined>()
+
+    expectTypeOf(
+      useSearch<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { hi: any },
+        true
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: { page?: number }) => {
+            hi: never
+          })
+        | undefined
+      >()
+
+    expectTypeOf(
+      useSearch<
+        DefaultRouter,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { hi: any },
+        true
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('structuralSharing')
+      .toEqualTypeOf<false | undefined>()
+
+    const routerWithStructuralSharing = createRouter({
+      routeTree,
+      defaultStructuralSharing: true,
+    })
+
+    expectTypeOf(
+      useSearch<
+        typeof routerWithStructuralSharing,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { func: () => void }
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: { page?: number }) => {
+            func: 'Function is not serializable'
+          })
+        | undefined
+      >()
+
+    expectTypeOf(
+      useSearch<
+        typeof routerWithStructuralSharing,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { date: () => void },
+        true
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('structuralSharing')
+      .toEqualTypeOf<false>()
+
+    expectTypeOf(
+      useSearch<
+        typeof routerWithStructuralSharing,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { hi: any },
+        true
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: { page?: number }) => {
+            hi: never
+          })
+        | undefined
+      >()
+
+    expectTypeOf(
+      useSearch<
+        typeof routerWithStructuralSharing,
+        '/invoices',
+        /* strict */ false,
+        /* shouldThrow */ true,
+        { hi: any },
+        true
+      >,
+    )
+      .parameter(0)
+      .toHaveProperty('structuralSharing')
+      .toEqualTypeOf<false>()
+  })
+
+  test('when there are multiple search params', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+      validateSearch: () => ({ page: 0 }),
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+      validateSearch: () => ({ detail: 'detail' }),
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
+      page: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { page: number }) => unknown) | undefined>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '/invoices', false>,
+    ).returns.toEqualTypeOf<{ page?: number; detail?: string }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        ((search: { page?: number; detail?: string }) => unknown) | undefined
+      >()
+  })
+
+  test('when there are overlapping search params', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+      validateSearch: () => ({ page: 0 }),
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+      validateSearch: () => ({ detail: 50 }) as const,
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+      validateSearch: () => ({ detail: 'detail' }) as const,
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
+      indexRoute,
+    ])
+
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
+      page: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { page: number }) => unknown) | undefined>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '/invoices', false>,
+    ).returns.toEqualTypeOf<{ page?: number; detail?: 'detail' | 50 }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: { page?: number; detail?: 'detail' | 50 }) => unknown)
+        | undefined
+      >()
+  })
+
+  test('when the root has no search params but the index route does', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      validateSearch: () => ({ indexPage: 0 }),
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{
+      indexPage: number
+    }>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '__root__'>,
+    ).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '/invoices'>,
+    ).returns.toEqualTypeOf<{}>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: {}) => unknown) | undefined>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '/invoices', false>,
+    ).returns.toEqualTypeOf<{ indexPage?: number }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        ((search: { indexPage?: number }) => unknown) | undefined
+      >()
+  })
+
+  test('when the root has search params but the index route does not', () => {
+    const rootRoute = createRootRoute({
+      validateSearch: () => ({ rootPage: 0 }),
+    })
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{
+      rootPage: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '__root__'>).returns.toEqualTypeOf<{
+      rootPage: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
+      rootPage: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { rootPage: number }) => unknown) | undefined>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '/invoices', false>,
+    ).returns.toEqualTypeOf<{ rootPage?: number }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { rootPage?: number }) => unknown) | undefined>()
+  })
+
+  test('when the root has search params but the index does', () => {
+    const rootRoute = createRootRoute({
+      validateSearch: () => ({ rootPage: 0 }),
+    })
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      validateSearch: () => ({ indexPage: 0 }),
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+    })
+
+    const invoicesIndexRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '/',
+    })
+
+    const invoiceRoute = createRoute({
+      getParentRoute: () => invoicesRoute,
+      path: '$invoiceId',
+    })
+
+    const routeTree = rootRoute.addChildren([
+      invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
+      indexRoute,
+    ])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const defaultRouter = createRouter({
+      routeTree,
+    })
+
+    type DefaultRouter = typeof defaultRouter
+
+    expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{
+      rootPage: number
+      indexPage: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '__root__'>).returns.toEqualTypeOf<{
+      rootPage: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
+      rootPage: number
+    }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<((search: { rootPage: number }) => unknown) | undefined>()
+
+    expectTypeOf(
+      useSearch<DefaultRouter, '/invoices', false>,
+    ).returns.toEqualTypeOf<{ indexPage?: number; rootPage?: number }>()
+
+    expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
+      .parameter(0)
+      .toHaveProperty('select')
+      .toEqualTypeOf<
+        | ((search: { indexPage?: number; rootPage?: number }) => unknown)
+        | undefined
+      >()
+  })
+
+  test('when a route has search params using SearchSchemaInput', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      validateSearch: (input: { page?: number } & SearchSchemaInput) => {
+        return { page: input.page ?? 0 }
+      },
+    })
+
+    const routeTree = rootRoute.addChildren([indexRoute])
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const router = createRouter({ routeTree })
+    expectTypeOf(useSearch<typeof router, '/'>).returns.toEqualTypeOf<{
+      page: number
+    }>
+  })
+
+  test('when route has a union of search params', () => {
+    const rootRoute = createRootRoute()
+
+    const postRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+      validateSearch: (): { status: 'in' } | { status: 'out' } => {
+        return { status: 'in' }
+      },
+    })
+
+    const indexRoute = createRoute({
+      getParentRoute: () => postRoute,
+      path: '/',
+      validateSearch: (): { detail: string } => {
+        return { detail: 'detail' }
+      },
+    })
+
+    const routeTree = rootRoute.addChildren([
+      indexRoute.addChildren([indexRoute]),
+    ])
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const router = createRouter({ routeTree })
+    expectTypeOf(useSearch<typeof router, '/'>).returns.toEqualTypeOf<
+      { status: 'in'; detail: string } | { status: 'out'; detail: string }
+    >
+  })
+
+  describe('shouldThrow', () => {
+    const rootRoute = createRootRoute()
+
+    const indexRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: '/',
+    })
+
+    const invoicesRoute = createRoute({
+      getParentRoute: () => rootRoute,
+      path: 'invoices',
+      validateSearch: () => ({ page: 0 }),
+    })
+
+    const routeTree = rootRoute.addChildren([invoicesRoute, indexRoute])
+
+    // eslint-disable-next-line unused-imports/no-unused-vars
+    const router = createRouter({ routeTree })
+
+    const from = '/invoices'
+    test('return type is `{ page: number }` when shouldThrow = true', () => {
+      const shouldThrow = true
+      const search = useSearch<
+        typeof router,
+        typeof from,
+        /* strict */ true,
+        typeof shouldThrow
+      >({ from, shouldThrow })
+
+      expectTypeOf(search).toEqualTypeOf<{ page: number }>()
+    })
+
+    test('return type is `{page: number} | undefined` when shouldThrow = false', () => {
+      const shouldThrow = false
+      const search = useSearch<
+        typeof router,
+        typeof from,
+        /* strict */ true,
+        typeof shouldThrow
+      >({ from, shouldThrow })
+
+      expectTypeOf(search).toEqualTypeOf<{ page: number } | undefined>()
+    })
   })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
-    page: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { page: number }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false>,
-  ).returns.toEqualTypeOf<{ page?: number }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { page?: number }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false, number>,
-  ).returns.toEqualTypeOf<number>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false, { func: () => void }>,
-  )
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      ((search: { page?: number }) => { func: () => void }) | undefined
-    >()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false, { func: () => void }>,
-  )
-    .parameter(0)
-    .toHaveProperty('structuralSharing')
-    .toEqualTypeOf<false | undefined>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false, { func: () => void }, true>,
-  )
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: { page?: number }) => {
-          func: 'Function is not serializable'
-        })
-      | undefined
-    >()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false, { func: () => void }, true>,
-  )
-    .parameter(0)
-    .toHaveProperty('structuralSharing')
-    .toEqualTypeOf<false | undefined>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices', false, { hi: any }, true>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: { page?: number }) => {
-          hi: never
-        })
-      | undefined
-    >()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices', false, { hi: any }, true>)
-    .parameter(0)
-    .toHaveProperty('structuralSharing')
-    .toEqualTypeOf<false | undefined>()
-
-  const routerWithStructuralSharing = createRouter({
-    routeTree,
-    defaultStructuralSharing: true,
-  })
-
-  expectTypeOf(
-    useSearch<
-      typeof routerWithStructuralSharing,
-      '/invoices',
-      false,
-      { func: () => void }
-    >,
-  )
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: { page?: number }) => {
-          func: 'Function is not serializable'
-        })
-      | undefined
-    >()
-
-  expectTypeOf(
-    useSearch<
-      typeof routerWithStructuralSharing,
-      '/invoices',
-      false,
-      { date: () => void },
-      true
-    >,
-  )
-    .parameter(0)
-    .toHaveProperty('structuralSharing')
-    .toEqualTypeOf<false>()
-
-  expectTypeOf(
-    useSearch<
-      typeof routerWithStructuralSharing,
-      '/invoices',
-      false,
-      { hi: any },
-      true
-    >,
-  )
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: { page?: number }) => {
-          hi: never
-        })
-      | undefined
-    >()
-
-  expectTypeOf(
-    useSearch<
-      typeof routerWithStructuralSharing,
-      '/invoices',
-      false,
-      { hi: any },
-      true
-    >,
-  )
-    .parameter(0)
-    .toHaveProperty('structuralSharing')
-    .toEqualTypeOf<false>()
-})
-
-test('when there are multiple search params', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-    validateSearch: () => ({ page: 0 }),
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
-    validateSearch: () => ({ detail: 'detail' }),
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
-    page: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { page: number }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false>,
-  ).returns.toEqualTypeOf<{ page?: number; detail?: string }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      ((search: { page?: number; detail?: string }) => unknown) | undefined
-    >()
-})
-
-test('when there are overlapping search params', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-    validateSearch: () => ({ page: 0 }),
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-    validateSearch: () => ({ detail: 50 }) as const,
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
-    validateSearch: () => ({ detail: 'detail' }) as const,
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    indexRoute,
-  ])
-
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
-    page: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { page: number }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false>,
-  ).returns.toEqualTypeOf<{ page?: number; detail?: 'detail' | 50 }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: { page?: number; detail?: 'detail' | 50 }) => unknown)
-      | undefined
-    >()
-})
-
-test('when the root has no search params but the index route does', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-    validateSearch: () => ({ indexPage: 0 }),
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{
-    indexPage: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '__root__'>).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices'>,
-  ).returns.toEqualTypeOf<{}>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: {}) => unknown) | undefined>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false>,
-  ).returns.toEqualTypeOf<{ indexPage?: number }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { indexPage?: number }) => unknown) | undefined>()
-})
-
-test('when the root has search params but the index route does not', () => {
-  const rootRoute = createRootRoute({
-    validateSearch: () => ({ rootPage: 0 }),
-  })
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{
-    rootPage: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '__root__'>).returns.toEqualTypeOf<{
-    rootPage: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
-    rootPage: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { rootPage: number }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false>,
-  ).returns.toEqualTypeOf<{ rootPage?: number }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { rootPage?: number }) => unknown) | undefined>()
-})
-
-test('when the root has search params but the index does', () => {
-  const rootRoute = createRootRoute({
-    validateSearch: () => ({ rootPage: 0 }),
-  })
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-    validateSearch: () => ({ indexPage: 0 }),
-  })
-
-  const invoicesRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: 'invoices',
-  })
-
-  const invoicesIndexRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '/',
-  })
-
-  const invoiceRoute = createRoute({
-    getParentRoute: () => invoicesRoute,
-    path: '$invoiceId',
-  })
-
-  const routeTree = rootRoute.addChildren([
-    invoicesRoute.addChildren([invoicesIndexRoute, invoiceRoute]),
-    indexRoute,
-  ])
-
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const defaultRouter = createRouter({
-    routeTree,
-  })
-
-  type DefaultRouter = typeof defaultRouter
-
-  expectTypeOf(useSearch<DefaultRouter, '/'>).returns.toEqualTypeOf<{
-    rootPage: number
-    indexPage: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '__root__'>).returns.toEqualTypeOf<{
-    rootPage: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>).returns.toEqualTypeOf<{
-    rootPage: number
-  }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices'>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<((search: { rootPage: number }) => unknown) | undefined>()
-
-  expectTypeOf(
-    useSearch<DefaultRouter, '/invoices', false>,
-  ).returns.toEqualTypeOf<{ indexPage?: number; rootPage?: number }>()
-
-  expectTypeOf(useSearch<DefaultRouter, '/invoices', false>)
-    .parameter(0)
-    .toHaveProperty('select')
-    .toEqualTypeOf<
-      | ((search: { indexPage?: number; rootPage?: number }) => unknown)
-      | undefined
-    >()
-})
-
-test('when a route has search params using SearchSchemaInput', () => {
-  const rootRoute = createRootRoute()
-
-  const indexRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-    validateSearch: (input: { page?: number } & SearchSchemaInput) => {
-      return { page: input.page ?? 0 }
-    },
-  })
-
-  const routeTree = rootRoute.addChildren([indexRoute])
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const router = createRouter({ routeTree })
-  expectTypeOf(useSearch<typeof router, '/'>).returns.toEqualTypeOf<{
-    page: number
-  }>
-})
-
-test('when route has a union of search params', () => {
-  const rootRoute = createRootRoute()
-
-  const postRoute = createRoute({
-    getParentRoute: () => rootRoute,
-    path: '/',
-    validateSearch: (): { status: 'in' } | { status: 'out' } => {
-      return { status: 'in' }
-    },
-  })
-
-  const indexRoute = createRoute({
-    getParentRoute: () => postRoute,
-    path: '/',
-    validateSearch: (): { detail: string } => {
-      return { detail: 'detail' }
-    },
-  })
-
-  const routeTree = rootRoute.addChildren([
-    indexRoute.addChildren([indexRoute]),
-  ])
-  // eslint-disable-next-line unused-imports/no-unused-vars
-  const router = createRouter({ routeTree })
-  expectTypeOf(useSearch<typeof router, '/'>).returns.toEqualTypeOf<
-    { status: 'in'; detail: string } | { status: 'out'; detail: string }
-  >
 })