useSearch
Compose query state, debounce, URL sync, and optional local search results.
useSearch is the reusable search state hook for Cubitt. It can run in query-only mode or local results mode.
Use it when you want one place to manage:
- the live input value
- the debounced value
- clear/reset behavior
- optional URL sync
- optional includes or fuzzy matching against local items
Query-Only Usage
import { Button } from "@tilt-legal/cubitt-components/button";
import { Input } from "@tilt-legal/cubitt-components/input";
import { SearchExpand } from "@tilt-legal/cubitt-components/search-expand";
import { useSearch } from "@tilt-legal/cubitt-components/utilities/hooks";
import { Xmark } from "@tilt-legal/cubitt-icons/ui/outline";
function ToolbarSearch() {
const search = useSearch({
debounceMs: 100,
paramName: "q",
});
return (
<SearchExpand expandedWidth="20rem">
<Input placeholder="Search..." {...search.inputProps} />
<Button
disabled={search.isEmpty}
mode="icon"
onClick={search.clear}
size="sm"
variant="link"
>
{!search.isEmpty && <Xmark />}
</Button>
</SearchExpand>
);
}In this mode, useSearch manages input state, debounce, and URL synchronization but does not calculate local results.
Local Includes Search
const search = useSearch({
items,
keys: ["name", "email"],
debounceMs: 100,
});
const visibleItems = search.results ?? items;The default strategy is "includes", which does a case-insensitive substring match across the configured keys.
Local Fuzzy Search
const search = useSearch({
strategy: "fuzzy",
items,
keys: ["name", "email", (item) => item.department],
debounceMs: 100,
paramName: "q",
});
const visibleItems = search.results ?? items;search.results is already ordered by relevance. If you need metadata as well, use search.rankedResults.
DataTable Integration
Use useSearch outside the table and pass the filtered rows into DataTable.
const search = useSearch<Row>({
strategy: "fuzzy",
items: data,
keys: ["name", "email", "department"],
debounceMs: 100,
});
<DataTable
columns={columns}
data={search.results ?? data}
enableSorting
/>This keeps search reusable and avoids a table-specific search component.
API
Options
| Option | Type | Default | Description |
|---|---|---|---|
value | string | - | Controlled search value |
defaultValue | string | "" | Initial value for uncontrolled mode |
onValueChange | (value: string) => void | - | Called when the value changes |
debounceMs | number | 0 | Debounce delay before debouncedValue updates |
items | readonly TItem[] | - | Optional local dataset to search |
strategy | "includes" | "fuzzy" | "custom" | "includes" | Search strategy to apply |
keys | SearchKey<TItem>[] | - | Keys or selectors used to derive searchable values |
matcher | (item: TItem, query: string) => boolean | - | Required when strategy is "custom" |
limit | number | - | Maximum number of returned results |
minQueryLength | number | 0 | Minimum trimmed query length before filtering starts |
paramName | string | - | URL query parameter key |
paramClearOnDefault | boolean | true | Remove the param when the value matches the default |
paramThrottle | number | - | Throttle URL updates |
paramDebounce | number | - | Debounce URL updates |
onUrlValueChange | (value: string | null) => void | - | Callback when the URL value changes |
Return Value
| Field | Type | Description |
|---|---|---|
value | string | Immediate input value |
debouncedValue | string | Debounced query value |
setValue | (value: string) => void | Imperatively update the query |
clear | () => void | Reset the query to an empty string |
isEmpty | boolean | true when value === "" |
hasQuery | boolean | true when the trimmed value is non-empty |
inputProps | { value; onChange } | Controlled props for a composed input |
results | TItem[] | undefined | Ordered visible items when items was provided |
rankedResults | SearchResult<TItem>[] | undefined | Ordered results plus rank metadata |
Notes
- When
useSearchowns URL sync, do not also pass URL-state props directly to the composedInput. useSearchdoes not render UI. It is designed to compose withSearchExpand,Input,Autocomplete, or custom inputs.matcheris only used for"custom"strategy.