Column filtering with operators and custom filters.
Filtering allows users to narrow down data by applying conditions to column values. The ActiveGrid supports text, number, date, select, and boolean filters with optional filter operators.
Enable filtering by setting the filterType in column metadata:
{
accessorKey: 'name',
header: 'Name',
meta: {
filterType: 'text',
},
}{
accessorKey: 'name',
header: 'Name',
meta: {
filterType: 'text',
},
}{
accessorKey: 'age',
header: 'Age',
meta: {
filterType: 'number',
},
}{
accessorKey: 'createdAt',
header: 'Created',
meta: {
filterType: 'date',
},
}{
accessorKey: 'status',
header: 'Status',
meta: {
filterType: 'select',
filterOptions: [
{ label: 'Active', value: 'active' },
{ label: 'Inactive', value: 'inactive' },
{ label: 'Pending', value: 'pending' },
],
},
}{
accessorKey: 'isActive',
header: 'Active',
meta: {
filterType: 'boolean',
booleanFilterLabels: {
all: 'All',
true: 'Active',
false: 'Inactive',
},
},
}Enable advanced filtering with operators:
{
accessorKey: 'price',
header: 'Price',
meta: {
filterType: 'number',
enableFilterOperators: true,
defaultFilterOperator: 'greaterThan',
},
}contains - Contains textnotContains - Does not contain textequals - Equals exactlynotEquals - Does not equalstartsWith - Starts with textendsWith - Ends with textisEmpty - Is emptyisNotEmpty - Is not emptyequals - Equals numbernotEquals - Does not equal numbergreaterThan - Greater thangreaterThanOrEqual - Greater than or equallessThan - Less thanlessThanOrEqual - Less than or equalbetween - Between two valuesisEmpty - Is emptyisNotEmpty - Is not emptyequals - On datebefore - Before dateafter - After dateonDate - On specific dateinRange - Between two datesisEmpty - Is emptyisNotEmpty - Is not emptyNew in v5.1
Available presets:
identity - equals, notEqualssearchOnly - contains, startsWith, endsWithrangeOnly - between, inRangecomparison - equals, notEquals, greaterThan, lessThannoEmpty - All operators except isEmpty/isNotEmptyNew in v5.1
Automatically generate filter options from data with counts:
Enable global search across all columns:
For server-side mode, filter state is passed to your fetch function:
For large datasets:
{
accessorKey: 'amount',
header: 'Amount',
meta: {
filterType: 'number',
enableFilterOperators: true,
operatorPreset: 'comparison', // equals, notEquals, greaterThan, lessThan
},
}{
accessorKey: 'price',
header: 'Price',
meta: {
filterType: 'number',
enableFilterOperators: true,
allowedOperators: ['equals', 'greaterThan', 'lessThan'],
},
}{
accessorKey: 'category',
header: 'Category',
meta: {
filterType: 'select',
filterOptions: [
{ label: 'Electronics', value: 'electronics' },
{ label: 'Clothing', value: 'clothing' },
{ label: 'Books', value: 'books' },
],
},
}{
accessorKey: 'country',
header: 'Country',
meta: {
filterType: 'select',
filterOptions: [...], // Long list
filterSearchable: true,
},
}{
accessorKey: 'status',
header: 'Status',
meta: {
filterType: 'select',
filterOptions: [...],
filterShowSelectAll: true,
},
}<ActiveGrid
mode="server"
fetchFn={async (params) => {
const response = await api.getData(params);
return {
data: response.data,
totalCount: response.total,
meta: {
facets: {
status: [
{ label: 'Active', value: 'active', count: 45 },
{ label: 'Inactive', value: 'inactive', count: 12 },
],
},
},
};
}}
{...}
/>{
accessorKey: 'tags',
header: 'Tags',
filterFn: (row, columnId, filterValue) => {
const tags = row.getValue(columnId) as string[];
return tags.some(tag =>
tag.toLowerCase().includes(filterValue.toLowerCase())
);
},
meta: {
filterType: 'text',
},
}<ActiveGrid
toolbar={{
search: {
placeholder: 'Search all columns...',
debounceMs: 300,
},
}}
{...}
/>import { useActiveGrid } from '@workspace/active-grid';
function MyComponent() {
const table = useActiveGrid();
// Set column filter
const filterByStatus = (status: string) => {
table.getColumn('status')?.setFilterValue(status);
};
// Clear column filter
const clearStatusFilter = () => {
table.getColumn('status')?.setFilterValue(undefined);
};
// Set global filter
const searchAll = (query: string) => {
table.setGlobalFilter(query);
};
// Clear all filters
const clearAllFilters = () => {
table.resetColumnFilters();
table.setGlobalFilter('');
};
}import { useState } from 'react';
import { ColumnFiltersState } from '@tanstack/react-table';
function MyComponent() {
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([
{ id: 'status', value: 'active' },
]);
return (
<ActiveGrid
columns={columns}
data={data}
state={{ columnFilters }}
onColumnFiltersChange={setColumnFilters}
{...}
/>
);
}<ActiveGrid
mode="server"
fetchFn={async (params) => {
// params.filters: [{ id: 'status', value: 'active' }]
// params.globalFilter: 'search query'
const response = await api.getUsers({
filters: params.filters,
search: params.globalFilter,
});
return {
data: response.data,
totalCount: response.total,
};
}}
{...}
/>// Text, number, date filters without operators
{ id: 'name', value: 'John' }// When operators are enabled
{
id: 'price',
value: {
operator: 'greaterThan',
value: 100,
},
}// Between/inRange operators
{
id: 'price',
value: {
operator: 'between',
value: 100,
valueTo: 500,
},
}const columns: GridColumnDef<Product>[] = [
{
accessorKey: 'name',
header: 'Product',
meta: {
filterType: 'text',
},
},
{
accessorKey: 'price',
header: 'Price',
meta: {
filterType: 'number',
enableFilterOperators: true,
operatorPreset: 'comparison',
},
},
{
accessorKey: 'category',
header: 'Category',
meta: {
filterType: 'select',
filterOptions: [
{ label: 'Electronics', value: 'electronics' },
{ label: 'Clothing', value: 'clothing' },
],
filterSearchable: true,
},
},
{
accessorKey: 'inStock',
header: 'In Stock',
meta: {
filterType: 'boolean',
booleanFilterLabels: {
all: 'All Items',
true: 'In Stock',
false: 'Out of Stock',
},
},
},
];{
accessorKey: 'createdAt',
header: 'Created',
meta: {
filterType: 'date',
enableFilterOperators: true,
defaultFilterOperator: 'inRange',
allowedOperators: ['before', 'after', 'inRange'],
},
}{
accessorKey: 'tags',
header: 'Tags',
filterFn: (row, columnId, filterValue: string[]) => {
if (!filterValue || filterValue.length === 0) return true;
const rowTags = row.getValue(columnId) as string[];
return filterValue.some(tag => rowTags.includes(tag));
},
meta: {
filterType: 'select',
filterOptions: [
{ label: 'Urgent', value: 'urgent' },
{ label: 'Important', value: 'important' },
{ label: 'Feature', value: 'feature' },
],
filterShowSelectAll: true,
},
}import { FilterFn } from '@tanstack/react-table';
type Product = {
name: string;
tags: string[];
price: number;
};
const tagsFilter: FilterFn<Product> = (row, columnId, filterValue) => {
const tags = row.getValue(columnId) as string[];
const searchTags = filterValue as string[];
return searchTags.some(tag => tags.includes(tag));
};
const columns: GridColumnDef<Product>[] = [
{
accessorKey: 'tags',
header: 'Tags',
filterFn: tagsFilter,
meta: {
filterType: 'select',
},
},
];