Data Table Filter
Faceted filter bar for data tables with submenu-based option selection, text filters, and operator support.
Made by eblairmckeePowered by
When to use
Use this decision tree to determine when to use the Data Table Filter component:
Installation
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.
Usage
import { DataTableFilter, type FilterColumnConfig, useDataTableFilter } from "@/components/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: ColumnDef<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 = useReactTable({
data,
columns: tableColumns,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
})
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 | Table<TData>? | TanStack Table 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 | Table<TData>? | TanStack Table instance (enables faceted counts) |
className | string? | Additional CSS classes |
variant | DataTableFilterVariant? | Badge variant applied to filter trigger, pills, and clear button |
DataTableFilterVariant
Controls the visual style of the filter trigger button, active filter pills, and the clear button using badge variant tokens.
Available values: neutral, neutral-inverted, neutral-outline, simple, simple-inverted, simple-outline, disabled, disabled-outline, outline, primary-inverted, primary-outline, secondary, secondary-inverted, secondary-outline.
Default: neutral-inverted for pills when no variant is specified; trigger and clear button remain unstyled.
<DataTableFilter
actions={actions}
columns={filterColumns}
filters={filters}
table={table}
variant="secondary-outline"
/>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.
Built by malinskibeniamin. The source code is available on GitHub.