---
title: useDataTableSearchParams
description: Sync TanStack Table V9 state through route-owned, validated TanStack Router search params.
icon: link-2
---

<PoweredBy packages={[
  { name: '@tanstack/react-router', url: 'https://tanstack.com/router' },
  { name: '@tanstack/react-table', url: 'https://tanstack.com/table' },
]} />

## Installation

```package-install
npx shadcn@latest add @redpanda/use-data-table-search-params
```

`useDataTableSearchParams` maps pagination, sorting, optional column filters, and optional global search to shareable URL state. The route validates search params; a provider adapts the typed route hooks to the reusable registry hook. Navigation merges previous search state and uses `replace: true`.

## Route setup

```tsx
import { createRoute } from "@tanstack/react-router"
import {
  DataTableSearchParamsProvider,
  validateDataTableSearchParams,
} from "@/hooks/use-data-table-search-params"

export const usersRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: "/users",
  validateSearch: validateDataTableSearchParams,
  component: UsersRoute,
})

function UsersRoute() {
  const search = usersRoute.useSearch()
  const navigate = usersRoute.useNavigate()

  return (
    <DataTableSearchParamsProvider search={search} navigate={navigate}>
      <UsersTable />
    </DataTableSearchParamsProvider>
  )
}
```

Keep `validateSearch` on the route that owns the table. Use that route's `useSearch` and `useNavigate` helpers so TanStack Router infers types without casts or `strict: false` access.

## Table setup

```tsx
import { DataTable } from "@/components/redpanda-ui/data-table"
import { useDataTableSearchParams } from "@/hooks/use-data-table-search-params"

function UsersTable() {
  const {
    pagination,
    onPaginationChange,
    sorting,
    onSortingChange,
    globalFilter,
    onGlobalFilterChange,
  } = useDataTableSearchParams({ syncGlobalFilter: true })

  return (
    <DataTable
      columns={columns}
      data={data}
      tableOptions={{
        state: { pagination, sorting, globalFilter },
        onPaginationChange,
        onSortingChange,
        onGlobalFilterChange,
      }}
    />
  )
}
```

## Multiple tables

Prefix keys when several tables share a route.

```tsx
import { createDataTableSearchParamsValidator } from "@/hooks/use-data-table-search-params"

const validateSearch = createDataTableSearchParamsValidator({
  prefixes: ["users", "teams"],
})

const usersState = useDataTableSearchParams({ paramPrefix: "users" })
const teamsState = useDataTableSearchParams({ paramPrefix: "teams" })
```

This produces keys such as `users_page`, `users_pageSize`, `users_globalFilter`, and `teams_sort`. Declare every prefix in the route validator so similarly named, unrelated route params remain untouched.

## First-page resets

Sorting, column-filter, global-filter, and page-size changes reset pagination to page 1 by default. The reset is merged into the same TanStack Router navigation, so the URL never exposes a transient stale page. Set `resetPageOnStateChange: false` only when the backing data source deliberately preserves page position.

## API

### `validateDataTableSearchParams(search)`

Use as TanStack Router's `validateSearch`. It normalizes positive page values, validates `column.direction` sorting segments, filter JSON, and string global filters, rejects malformed table state, and preserves unrelated route search params.

### `createDataTableSearchParamsValidator({ prefixes })`

Create a `validateSearch` function for routes with prefixed table state. Only unprefixed table keys and keys matching the declared prefixes are validated; all other search params pass through unchanged.

### `DataTableSearchParamsProvider`

| Prop | Type | Description |
| --- | --- | --- |
| `search` | `DataTableSearchParams` | Value returned by the owning route's `useSearch`. |
| `navigate` | `DataTableSearchParamsNavigate` | Typed adapter around the owning route's `useNavigate`. |
| `children` | `ReactNode` | Tables that consume URL state. |

### `useDataTableSearchParams(options?)`

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `syncPagination` | `boolean` | `true` | Sync pagination. |
| `syncSorting` | `boolean` | `true` | Sync sorting. |
| `syncFilters` | `boolean` | `false` | Sync column filters. |
| `syncGlobalFilter` | `boolean` | `false` | Sync the V9 global-filter slice. |
| `paramPrefix` | `string` | — | Namespace keys for one table. |
| `defaultPagination` | `PaginationState` | `{ pageIndex: 0, pageSize: 10 }` | State used when the URL omits pagination. |
| `defaultSorting` | `SortingState` | `[]` | State used when the URL omits sorting. |
| `defaultGlobalFilter` | `string` | `""` | State used when the URL omits global search. |
| `resetPageOnStateChange` | `boolean` | `true` | Reset to page 1 atomically when sorting, filtering, global search, or page size changes. |

Returns TanStack Table-compatible state slices and `onChange` handlers. Pass them through `DataTable`'s `tableOptions`.

## Related

For token APIs that cannot express offsets, use [`useTokenPagination`](/docs/use-token-pagination).
