Data Search
A debounced global-search input over a consumer-owned useReactTable() instance — matches primitive cell values across every searchable column.
| Title | Status | Priority |
|---|---|---|
| Design onboarding flow | In Progress | 2 |
| Fix invoice rounding bug | Done | 1 |
| Write Q3 report | Todo | 3 |
| Review PR #482 | In Progress | 1 |
DataSearch is a headless-composition component over the same
useReactTable() instance you'd pass to Data Grid
and Data Filter — a sibling toolbar control,
not a prop on either. It drives TanStack's own globalFilter state instead of
a parallel search implementation.
Installation
pnpm dlx shadcn@latest add @syncblocks/data-searchUsage
import {
DataSearch,
dataGridGetColumnCanGlobalFilter,
dataGridGlobalFilterFn,
} from "@/components/ui/data-search"const table = useReactTable({
data,
columns,
globalFilterFn: dataGridGlobalFilterFn,
getColumnCanGlobalFilter: dataGridGetColumnCanGlobalFilter,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
})
<DataSearch table={table} />The table needs all three: globalFilterFn: dataGridGlobalFilterFn,
getColumnCanGlobalFilter: dataGridGetColumnCanGlobalFilter, and
getFilteredRowModel() — DataSearch only writes globalFilter state, it
never filters rows itself.
getColumnCanGlobalFilter isn't optional the way it might look. TanStack's
own default implementation decides which columns are even considered for
global search by reading each column's first row value and checking
typeof value === "string" || typeof value === "number" — before
globalFilterFn ever runs. An array-valued multiSelect column fails that
check and is silently dropped from search entirely, no matter what
dataGridGlobalFilterFn would have matched. dataGridGetColumnCanGlobalFilter
replaces that heuristic with one that understands the same meta.type
contract as Data Filter: every typed column
stays eligible except boolean (not meaningfully free-text-searchable), and
untyped columns fall back to TanStack's default. Excluding a specific column
from search on top of that is an ordinary TanStack concern
(enableGlobalFilter: false), not a second config list.
Features
- Debounced input — keystrokes update local state immediately; the
200ms-debounced value is what actually reaches
table.setGlobalFilter(), so large datasets don't re-filter on every keystroke. dataGridGlobalFilterFn— the exportedglobalFilterFn. TanStack calls it once per column per row and ORs the results, so a row matches if any column matches. It readscolumn.meta.typethe same way Data Filter does:selectanduserresolve the stored value to its option label / user name before matching (searching "priya" finds ausercell storing id"u3"ifmeta.usershas{ id: "u3", name: "Priya Patel" }),multiSelectmatches if any tag's label matches, and ausercell with anullvalue matches the literal string "unassigned". Every other type stringifies the raw cell value and matches case-insensitively; columns with no accessor value (id/action columns) never match.dataGridGetColumnCanGlobalFilter— the exportedgetColumnCanGlobalFilter, required alongsidedataGridGlobalFilterFn(see Usage above) so array-valuedmultiSelectcolumns aren't dropped from search beforedataGridGlobalFilterFneven runs.- Clear button — appears once there's a value; clears local state and
globalFiltertogether. - Stays in sync with external clears — if something else resets
globalFilter(e.g. a "Clear all" action elsewhere), the input's local state follows it via thetable.getState().globalFilterprop, not just its own button.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
table | Table<TData> | - | A useReactTable() instance you create and own — the same one you'd pass to DataGrid/DataFilter. Needs globalFilterFn: dataGridGlobalFilterFn and getFilteredRowModel(). (required) |
placeholder | string | "Search..." | Input placeholder text. |