Data Table Filter
Faceted filter bar for data tables with submenu-based option selection, text filters, and operator support.
Powered by
Installation
Requires React and React DOM 19.2 or later. The copied item declares this API floor; its text-filter debounce uses a local Effect Event so callback changes do not reset the user’s pending edit.
npx shadcn@latest add @redpanda/data-table-filterpnpm dlx shadcn@latest add @redpanda/data-table-filteryarn dlx shadcn@latest add @redpanda/data-table-filterbunx shadcn@latest add @redpanda/data-table-filternubx shadcn@latest add @redpanda/data-table-filteraube dlx shadcn@latest add @redpanda/data-table-filterWhen to use
Use this decision tree to determine when to use the Data Table Filter component:
Install the TanStack Table peer dependency if you haven’t already:
npm install @tanstack/react-tableVariants
Use the variant prop to style the filter trigger, active filter pills, and clear button with badge styling.
Keyboard and accessibility
Add, clear, operator, value, and remove actions are native buttons that do not submit a surrounding form. Operator and value controls announce the column they edit and describe the current operator or values. Open a control with Enter or Space, navigate its options with the arrow keys, select with Enter, and close with Escape to return focus to the trigger. Option checkmarks are decorative: the command option is the only selection target, and its description identifies values already included in the filter.
Usage
import { DataTableFilter, type FilterColumnConfig } from '@/components/redpanda-ui/data-table-filter';import { useDataTable, type DataTableColumnDef } from '@/components/redpanda-ui/data-table';import { useDataTableFilter } from '@/hooks/use-data-table-filter';import { createFilterFn } from '@/lib/filter-utils';1. Define filter columns
Describe which columns are filterable, what type of filter they use, and the available options:
const filterColumns: FilterColumnConfig[] = [ { id: 'status', type: 'option', displayName: 'Status', icon: CircleDotIcon, options: [ { value: 'open', label: 'Open', icon: CircleIcon }, { value: 'closed', label: 'Closed', icon: CircleCheckIcon }, ], }, { id: 'assignee', type: 'option', displayName: 'Assignee', icon: UserIcon, options: [ { value: 'alice', label: 'Alice' }, { value: 'bob', label: 'Bob' }, ], }, { id: 'title', type: 'text', displayName: 'Title', placeholder: 'Search titles…', },];2. Wire up TanStack Table columns
Each filterable table column needs a custom filterFn created with createFilterFn:
import { createFilterFn } from '@/lib/filter-utils';
const tableColumns: DataTableColumnDef<Task>[] = [ { accessorKey: 'title', header: 'Title', filterFn: createFilterFn('text') }, { accessorKey: 'status', header: 'Status', filterFn: createFilterFn('option') }, { accessorKey: 'assignee', header: 'Assignee', filterFn: createFilterFn('option') },];3. Connect the hook and render
function TaskTable({ data }: { data: Task[] }) { const table = useDataTable({ data, columns: tableColumns, });
const { filters, actions } = useDataTableFilter<Task>({ columns: filterColumns, table, });
return ( <div> <DataTableFilter actions={actions} columns={filterColumns} filters={filters} table={table} /> {/* render your <Table /> below */} </div> );}Filter types
| Type | Operators | Description |
|---|---|---|
text |
contains, does not contain |
Free-text search against a column value |
option |
is, is not, is any of, is none of |
Single-select enum column with automatic operator transition |
multiOption |
include, exclude, include any of, include all of |
Multi-value column (e.g. tags, labels) |
Operators auto-transition when the number of selected values changes. For example, selecting a second value in an option filter automatically switches from is to is any of.
Text filters
Text filters provide free-text search with contains / does not contain operators.
Controlled state
Use the value and onValueChange props on useDataTableFilter to manage filter state externally. This is useful for URL persistence, undo/redo, or syncing with external state.
URL persistence with nuqs
Use nuqs to sync filter state with URL search params so filters survive page refreshes and can be shared via links:
import { parseAsJson, useQueryState } from 'nuqs';import { type FiltersState } from '@/lib/filter-utils';
function TaskTable({ data }: { data: Task[] }) { const [filters, setFilters] = useQueryState<FiltersState>('filters', parseAsJson<FiltersState>().withDefault([]));
const { actions } = useDataTableFilter<Task>({ columns: filterColumns, table, value: filters, onValueChange: setFilters, });
return <DataTableFilter actions={actions} columns={filterColumns} filters={filters} table={table} />;}API Reference
FilterColumnConfig
Configuration for each filterable column.
| Prop | Type | Description |
|---|---|---|
id |
string |
Column ID matching a TanStack Table column accessorKey |
type |
"text" | "option" | "multiOption" |
Filter type |
displayName |
string |
Human-readable label shown in the filter UI |
displayNamePlural |
string? |
Plural form for display (defaults to displayName) |
icon |
ComponentType? |
Icon shown next to the filter name |
options |
FilterOption[]? |
Available options (for option and multiOption types) |
placeholder |
string? |
Placeholder text (for text type) |
useDataTableFilter<TData>
Hook that manages filter state and syncs it to a TanStack Table instance.
| Option | Type | Description |
|---|---|---|
columns |
FilterColumnConfig[] |
Filter column definitions |
table |
DataTableInstance<TData>? |
Feature-bound TanStack Table V9 instance to sync filters to |
value |
FiltersState? |
Controlled filter state |
onValueChange |
(filters: FiltersState) => void |
Callback when filters change (controlled mode) |
defaultValue |
FiltersState? |
Initial filter state (uncontrolled mode) |
Returns { filters, actions, columns }.
DataTableFilterActions
Actions returned by the hook for manipulating filter state.
| Method | Signature | Description |
|---|---|---|
addFilter |
(columnId: string) => void |
Add an empty filter for a column |
removeFilter |
(columnId: string) => void |
Remove the filter for a column |
removeAllFilters |
() => void |
Clear all active filters |
setFilterValues |
(columnId: string, values: string[]) => void |
Set the selected values for a filter |
addFilterValue |
(columnId: string, value: string) => void |
Add a value to a filter |
removeFilterValue |
(columnId: string, value: string) => void |
Remove a value from a filter |
setFilterOperator |
(columnId: string, operator: string) => void |
Change the operator for a filter |
DataTableFilter
The filter bar component.
| Prop | Type | Description |
|---|---|---|
columns |
FilterColumnConfig[] |
Filter column definitions |
filters |
FiltersState |
Current filter state |
actions |
DataTableFilterActions |
Actions from useDataTableFilter |
table |
DataTableInstance<TData>? |
Feature-bound TanStack Table V9 instance (enables faceted counts) |
className |
string? |
Additional CSS classes |
tone |
BadgeTone? |
Semantic color shared by the trigger, pills, and clear button; default primary |
variant |
DataTableFilterVariant? |
Emphasis: solid, subtle (default), or outline |
DataTableFilterVariant
Controls emphasis for the trigger button, active pills, and clear button. Choose
solid, subtle, or outline, paired with a semantic tone.
The default remains the primary subtle treatment. Flat Badge style keys, including
primary-inverted, default-outline, and the disabled paint-only variants, are
removed. Disabled-looking paint is not an interaction-disabling API.
<DataTableFilter actions={actions} columns={filterColumns} filters={filters} table={table} tone="default" variant="outline"/>Additional patterns
URL-synced filters
Inline filter validation
Related
- Filtered Table Pattern — Full toolbar layout with DataTableFilter and table actions
- Data Table — TanStack Table-based data grid
Credits
- Inspired by bazza.dev Data Table Filter for the filter architecture and operator model. See also: ui.bazza.dev documentation.
- Uses @tanstack/react-table for faceted filtering and row model integration.