diff --git a/examples/react/basic-default-search-params/src/main.tsx b/examples/react/basic-default-search-params/src/main.tsx
index b79a6398301..75ed3a6ebf2 100644
--- a/examples/react/basic-default-search-params/src/main.tsx
+++ b/examples/react/basic-default-search-params/src/main.tsx
@@ -172,12 +172,18 @@ function PostErrorComponent({ error }: ErrorRouteProps) {
function PostComponent() {
const post = postRoute.useLoaderData()
- const { color } = postRoute.useSearch()
+ const { color, postId } = postRoute.useSearch()
+ const setSearch = postRoute.useSetSearch({ replace: false })
+
return (
{post.title}
{post.body}
+
+
)
}
diff --git a/packages/react-router/src/index.tsx b/packages/react-router/src/index.tsx
index 6841e31823f..899129eba61 100644
--- a/packages/react-router/src/index.tsx
+++ b/packages/react-router/src/index.tsx
@@ -26,4 +26,5 @@ export * from './useBlocker'
export * from './useNavigate'
export * from './useParams'
export * from './useSearch'
+export * from './useSetSearch'
export * from './utils'
diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts
index 3245b5c9519..ca9142fd7f1 100644
--- a/packages/react-router/src/route.ts
+++ b/packages/react-router/src/route.ts
@@ -9,6 +9,7 @@ import { RouteById, RouteIds, RoutePaths } from './routeInfo'
import { AnyRouter, RegisteredRouter } from './router'
import { useParams } from './useParams'
import { useSearch } from './useSearch'
+import { useSetSearch } from './useSetSearch'
import {
Assign,
Expand,
@@ -504,6 +505,12 @@ export class RouteApi<
return useSearch({ ...opts, from: this.id } as any)
}
+ useSetSearch = (opts?: {
+ replace?: boolean
+ }): ((search: Partial) => void) => {
+ return useSetSearch({ ...opts, from: this.id } as any) as any
+ }
+
useParams = (opts?: {
select?: (s: TAllParams) => TSelected
}): TSelected => {
@@ -829,6 +836,12 @@ export class Route<
return useSearch({ ...opts, from: this.id } as any)
}
+ useSetSearch = (opts?: {
+ replace?: boolean
+ }): ((search: Partial) => void) => {
+ return useSetSearch({ ...opts, from: this.id } as any) as any
+ }
+
useParams = (opts?: {
select?: (search: TAllParams) => TSelected
}): TSelected => {
diff --git a/packages/react-router/src/useSetSearch.tsx b/packages/react-router/src/useSetSearch.tsx
new file mode 100644
index 00000000000..83fce4da9da
--- /dev/null
+++ b/packages/react-router/src/useSetSearch.tsx
@@ -0,0 +1,44 @@
+import { AnyRoute } from './route'
+import { RouteIds, RouteById } from './routeInfo'
+import { RegisteredRouter } from './router'
+import { RouteMatch } from './Matches'
+import { useMatch } from './Matches'
+import { useRouter } from './RouterProvider'
+import React from 'react'
+
+export function useSetSearch<
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
+ TFrom extends RouteIds = RouteIds,
+ TSearch extends Record = RouteById<
+ TRouteTree,
+ TFrom
+ >['types']['fullSearchSchema'],
+>(opts: {
+ from: TFrom
+ replace: boolean | undefined
+}): (search: Partial) => void {
+ const router = useRouter()
+
+ const prevSearch = useMatch({
+ from: opts.from,
+ select: (match: RouteMatch) => {
+ return match.search
+ },
+ })
+
+ return React.useCallback((search: Partial) => {
+ const __tempSearch = { ...prevSearch, ...search }
+ const { hash, state, pathname } = router.state.location
+ const searchStr = router.options.stringifySearch(__tempSearch)
+
+ router.commitLocation({
+ replace: opts.replace ?? true,
+ pathname,
+ hash,
+ href: `${pathname}${searchStr}${hash}`,
+ search: __tempSearch,
+ searchStr,
+ state,
+ })
+ }, [])
+}