Advanced Filter Table
Rule-based filtering for complex datasets where users can define custom rules for data visibility.
Preview with Controlled State
View Full State Object
No enhanced filters
{
"left": [],
"right": []
}{
"totalFilters": 0,
"hasAndFilters": false,
"hasOrFilters": false,
"effectiveJoinOperator": "and",
"activeFilters": 0
}[]
[]
""
{}Introduction
Section titled “Introduction”The Advanced Table demonstrates rule-based filtering with DataTableFilterMenu and DataTableSortMenu for complex filtering scenarios. Users can create multiple filter rules, combine them with AND/OR operators, and manage sorting with a visual interface.
Installation
Section titled “Installation”Install the DataTable core and add-ons for this example:
This example also uses checkbox from Shadcn UI:
First time using
@niko-table? See the Installation Guide to set up the registry.
For other add-ons or manual copy-paste, see the Installation Guide.
Prerequisites
Section titled “Prerequisites”We are going to build a table to show products with advanced filtering. Here’s what our data looks like:
type Product = { id: string name: string category: string brand: string price: number stock: number rating: number inStock: boolean releaseDate: Date}
const categoryOptions = [ { label: "Electronics", value: "electronics" }, { label: "Clothing", value: "clothing" }, { label: "Sports", value: "sports" },]
// Brand options can be auto-generated from data using meta.autoOptionsBasic Table with Advanced Filters
Section titled “Basic Table with Advanced Filters”Let’s start by building a table with rule-based filtering.
Column Definitions
Section titled “Column Definitions”First, we’ll define our columns with filter metadata.
"use client"
import { DataTableColumnHeader, DataTableColumnTitle,} from "@/components/niko-table/components/data-table-column-header"import { DataTableColumnSortMenu } from "@/components/niko-table/components/data-table-column-sort"import type { DataTableColumnDef } from "@/components/niko-table/types"
export type Product = { id: string name: string category: string brand: string price: number stock: number rating: number inStock: boolean releaseDate: Date}
const categoryOptions = [ { label: "Electronics", value: "electronics" }, { label: "Clothing", value: "clothing" }, { label: "Sports", value: "sports" },]
const brandOptions = [ { label: "Apple", value: "apple" }, { label: "Samsung", value: "samsung" }, { label: "Nike", value: "nike" },]
export const columns: DataTableColumnDef<Product>[] = [ { accessorKey: "name", header: () => ( <DataTableColumnHeader> <DataTableColumnTitle /> <DataTableColumnSortMenu /> </DataTableColumnHeader> ), meta: { label: "Product Name", variant: "text", }, enableColumnFilter: true, }, { accessorKey: "category", header: () => ( <DataTableColumnHeader> <DataTableColumnTitle /> <DataTableColumnSortMenu /> </DataTableColumnHeader> ), meta: { label: "Category", variant: "select", options: categoryOptions, mergeStrategy: "augment", dynamicCounts: true, showCounts: true, }, enableColumnFilter: true, }, { accessorKey: "brand", header: () => ( <DataTableColumnHeader> <DataTableColumnTitle /> <DataTableColumnSortMenu /> </DataTableColumnHeader> ), meta: { label: "Brand", variant: "select", autoOptions: true, dynamicCounts: true, showCounts: true, }, enableColumnFilter: true, }, { accessorKey: "price", header: () => ( <DataTableColumnHeader> <DataTableColumnTitle /> <DataTableColumnSortMenu /> </DataTableColumnHeader> ), meta: { label: "Price", variant: "number", unit: "$", }, enableColumnFilter: true, },]<DataTable /> component
Section titled “<DataTable /> component”Next, we’ll add the filter and sort menu components.
"use client"
import { DataTableRoot } from "@/components/niko-table/core/data-table-root"import { DataTable } from "@/components/niko-table/core/data-table"import { DataTableHeader, DataTableBody, DataTableEmptyBody,} from "@/components/niko-table/core/data-table-structure"import { DataTableToolbarSection } from "@/components/niko-table/components/data-table-toolbar-section"import { DataTablePagination } from "@/components/niko-table/components/data-table-pagination"import { DataTableSearchFilter } from "@/components/niko-table/components/data-table-search-filter"import { DataTableViewMenu } from "@/components/niko-table/components/data-table-view-menu"import { DataTableSortMenu } from "@/components/niko-table/components/data-table-sort-menu"import { DataTableFilterMenu } from "@/components/niko-table/components/data-table-filter-menu"import { DataTableColumnHeader, DataTableColumnTitle,} from "@/components/niko-table/components/data-table-column-header"import { DataTableColumnSortMenu } from "@/components/niko-table/components/data-table-column-sort"import type { DataTableColumnDef } from "@/components/niko-table/types"
type Product = { id: string name: string category: string brand: string price: number}
const columns: DataTableColumnDef<Product>[] = [ { accessorKey: "name", header: () => ( <DataTableColumnHeader> <DataTableColumnTitle /> <DataTableColumnSortMenu /> </DataTableColumnHeader> ), meta: { label: "Product Name", variant: "text", }, enableColumnFilter: true, }, { accessorKey: "category", header: () => ( <DataTableColumnHeader> <DataTableColumnTitle /> <DataTableColumnSortMenu /> </DataTableColumnHeader> ), meta: { label: "Category", variant: "select", options: [ { label: "Electronics", value: "electronics" }, { label: "Clothing", value: "clothing" }, ], }, enableColumnFilter: true, }, { accessorKey: "price", header: () => ( <DataTableColumnHeader> <DataTableColumnTitle /> <DataTableColumnSortMenu /> </DataTableColumnHeader> ), meta: { label: "Price", variant: "number", unit: "$", }, enableColumnFilter: true, },]
function FilterToolbar() { return ( <DataTableToolbarSection> <DataTableToolbarSection className="px-0"> <DataTableSearchFilter placeholder="Search products..." /> <DataTableViewMenu /> </DataTableToolbarSection> <DataTableToolbarSection className="px-0"> <DataTableSortMenu className="ml-auto" /> <DataTableFilterMenu autoOptions dynamicCounts showCounts mergeStrategy="augment" /> </DataTableToolbarSection> </DataTableToolbarSection> )}
export function AdvancedTable({ data }: { data: Product[] }) { return ( <DataTableRoot data={data} columns={columns} config={{ enablePagination: true, enableSorting: true, enableMultiSort: true, enableFilters: true, }} > <FilterToolbar /> <DataTable> <DataTableHeader /> <DataTableBody> <DataTableEmptyBody /> </DataTableBody> </DataTable> <DataTablePagination /> </DataTableRoot> )}Filter Components
Section titled “Filter Components”DataTableFilterMenu
Section titled “DataTableFilterMenu”The filter menu component allows users to create rule-based filters with multiple conditions.
Props:
| Name | Type | Default | Description |
|---|---|---|---|
autoOptions | boolean | true | Auto-generate options for select/multiSelect columns |
showCounts | boolean | true | Show counts beside each option |
dynamicCounts | boolean | true | Recompute counts based on currently filtered rows |
limitToFilteredRows | boolean | true | Generate options from filtered rows only (vs all rows) |
includeColumns | string[] | - | Only generate options for these column ids |
excludeColumns | string[] | - | Exclude these column ids from generation |
limitPerColumn | number | - | Limit number of generated options per column |
mergeStrategy | "preserve" | "augment" | "replace" | "preserve" | How to handle existing static options |
filters? | ExtendedColumnFilter[] | - | Array of filters (for controlled mode) |
onFiltersChange? | (filters: ExtendedColumnFilter[] | null) => void | - | Callback when filters change |
className | string | - | Additional CSS classes |
Merge Strategy:
preserve: Keep user-defined options untouched (default)augment: Add counts to matching values from static optionsreplace: Override static options with generated options
<DataTableFilterMenu autoOptions dynamicCounts showCounts limitToFilteredRows={true} mergeStrategy="augment"/>DataTableSortMenu
Section titled “DataTableSortMenu”The sort menu component provides a visual interface for managing column sorting.
Props:
| Name | Type | Description |
|---|---|---|
className | string | Additional CSS classes |
<DataTableSortMenu className="ml-auto" />Column Meta for Filters
Section titled “Column Meta for Filters”Define filter metadata in your column definitions:
{ accessorKey: "category", meta: { label: "Category", variant: "select", options: [ { label: "Electronics", value: "electronics" }, { label: "Clothing", value: "clothing" }, ], }, enableColumnFilter: true,}Supported variants:
"text"- Text input filter"select"- Dropdown select filter (requiresoptions)"multiSelect"- Multiple selection dropdown"number"- Number input filter (optionalunitfor display)"range"- Numeric range slider (auto-applied fornumbervariant withmeta.range)"boolean"- Boolean checkbox filter"date"- Single date picker filter"dateRange"- Date range picker (auto-applied whenDataTableDateFilterhasmultiple={true})
Auto-applied filter functions:
When you specify meta.variant in your column definition, the appropriate filterFn is automatically applied:
variant: "range"→numberRangeFilter(for numeric ranges like price)variant: "date"orvariant: "dateRange"→dateRangeFilter(for date filtering)
You can still provide a custom filterFn if needed - it will override the auto-applied function.
Controlled State
Section titled “Controlled State”Full control over table state:
import { useState } from "react"import type { PaginationState, SortingState, ColumnFiltersState,} from "@tanstack/react-table"import type { ExtendedColumnFilter } from "@/components/niko-table/types"
export function ControlledAdvancedTable({ data }: { data: Product[] }) { const [globalFilter, setGlobalFilter] = useState<string | object>("") const [sorting, setSorting] = useState<SortingState>([]) const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]) const [pagination, setPagination] = useState<PaginationState>({ pageIndex: 0, pageSize: 10, })
const handleFiltersChange = ( filters: ExtendedColumnFilter<Product>[] | null, ) => { if (!filters || filters.length === 0) { setColumnFilters([]) } else { setColumnFilters( filters.map(filter => ({ id: filter.id, value: filter, })), ) } }
return ( <DataTableRoot data={data} columns={columns} config={{ enablePagination: true, enableSorting: true, enableMultiSort: true, enableFilters: true, }} state={{ globalFilter, sorting, columnFilters, pagination, }} onGlobalFilterChange={setGlobalFilter} onSortingChange={setSorting} onColumnFiltersChange={setColumnFilters} onPaginationChange={setPagination} > <DataTableToolbarSection> <DataTableToolbarSection className="px-0"> <DataTableSearchFilter placeholder="Search products..." /> <DataTableViewMenu /> </DataTableToolbarSection> <DataTableToolbarSection className="px-0"> <DataTableSortMenu className="ml-auto" /> <DataTableFilterMenu filters={[]} onFiltersChange={handleFiltersChange} /> </DataTableToolbarSection> </DataTableToolbarSection> <DataTable> <DataTableHeader /> <DataTableBody> <DataTableEmptyBody /> </DataTableBody> </DataTable> <DataTablePagination /> </DataTableRoot> )}When to Use
Section titled “When to Use”✅ Use Advanced Table when:
- Users need complex, rule-based filtering
- You want visual filter and sort management
- Multiple filter conditions need to be combined
- You need drag-and-drop sorting interface
- Filter state needs to be managed externally
❌ Consider other options when:
- Simple filtering is sufficient (use Faceted Filter Table)
- You prefer inline filters (use Advanced Inline Filter Table)
- You need URL state persistence (use Advanced Nuqs Table)
Next Steps
Section titled “Next Steps”- Advanced Inline Filter Table - Build custom inline filter UIs
- Advanced Nuqs Table - Add URL state persistence