Filtering
Filter types, operators, presets, date filters with time zones, and the global search.
@workspace/active-grid 11.0.0Status unknowncontent differs from the recorded release 11.0.0baseline ce081dc3
This guide is the package's docs/, included unchanged from
the package as it is in Foundation. Links to the package's internal design
documents keep their text.
How to configure column filters in Active Grid: filter types, operators,
presets, date filters (including time zones and calendar locales) and the
global search. Type signatures, operator factories and filter components are in
the API reference: ../.
Filter Types#
Active Grid provides the following standard filter widgets, configured via meta. on
column definitions:
| Type | Component | Operators | Use Case |
|---|---|---|---|
text | Text | 8 operators | Names, descriptions, codes |
number | Number | 7 default operators | Amounts, quantities, percentages |
date | Date | 4 default operators | Dates and timestamps |
select | Select | Multi-select | Enum values, categories, statuses |
boolean | Boolean | 1 default operator | Yes/no flags |
filter chooses the widget; the capability decides whether there is one
at all. Both the filter row and each filter cell require
column. as well. In client mode that is true by default, so the
table above reads as it always did. In server mode a column without a
server. mapping (or without an entry in the server filter
map) renders no control, whatever its filter says — including a
filter injected by a column type preset. Presentation may narrow the
server contract, never widen it. See
../ § Filtering.
An additional interval type accepts numeric from/to bounds and a
within/overlap mode through Interval; its host supplies the
interval input UI. The tables below include optional absence operators, which
are not in the default number/date/boolean lists (see with).
Text Operators#
| Operator | Label | Requires Value | Description |
|---|---|---|---|
contains | Contains | Yes | Case-insensitive substring match |
not | Does not contain | Yes | Inverse of contains |
equals | Equals | Yes | Exact match (case-insensitive) |
not | Does not equal | Yes | Inverse of equals |
starts | Starts with | Yes | Prefix match |
ends | Ends with | Yes | Suffix match |
is | Is empty | No | null, undefined, or '' |
is | Is not empty | No | Inverse of isEmpty |
Number Operators#
| Operator | Label | Requires Value | Description |
|---|---|---|---|
equals | Equals | Yes | Exact numeric match |
not | Does not equal | Yes | Inverse of equals |
greater | Greater than | Yes | Strictly greater |
greater | Greater than or equal | Yes | Greater or equal |
less | Less than | Yes | Strictly less |
less | Less than or equal | Yes | Less or equal |
between | Between | Yes (two values) | Inclusive range: value >= from && value <= to |
is | Is empty | No | null or undefined |
is | Is not empty | No | Inverse of isEmpty |
Date Operators#
| Operator | Label | Requires Value | Description |
|---|---|---|---|
on | On date | Yes | Exact date match (day precision) |
before | Before | Yes | Strictly before the given date |
after | After | Yes | Strictly after the given date |
in | In range | Yes (two values) | Between start and end date (inclusive) |
is | Is empty | No | null or undefined |
is | Is not empty | No | Inverse of isEmpty |
Boolean Operators#
| Operator | Label | Requires Value | Description |
|---|---|---|---|
equals | Is | Yes | Match true or false |
is | Is empty | No | null or undefined |
is | Is not empty | No | Inverse of isEmpty |
Select Filter#
The select filter uses a multi-select dropdown rather than operators. Users select one or more values from a list.
Filter value is an array of selected values: ['active', 'pending'].
Options come from (in priority order):
meta.— Explicit options array.filter Options Server-side
facets— Returned byfetchin server mode.Fn Client-side faceted values — Auto-computed from column data.
Configuration:
{ meta: { filterType: 'select', filterOptions: [ {label: 'Active', value: 'active'}, {label: 'Inactive', value: 'inactive'}, ], filterSearchable: true, // Enable search within options filterShowSelectAll: true, // Show "Select all" toggle },}FilterValue Type#
Filter values are discriminated unions based on the operator:
// No-value operators (isEmpty, isNotEmpty){operator: 'isEmpty'}
// Single-value operators{operator: 'contains', value: 'john'}{operator: 'greaterThan', value: 50000}{operator: 'onDate', value: '2025-06-15'}{operator: 'equals', value: true}
// Range operators (between, inRange){operator: 'between', value: 1000, valueTo: 5000}{operator: 'inRange', value: '2025-01-01', valueTo: '2025-12-31'}TypeScript enforces the shape through a discriminated union; the type
definitions are in ../ § FilterValue Types.
Operator Presets#
A preset restricts the operators a column offers. Select one by name with
meta.:
{meta: {filterType: 'text', operatorPreset: 'identity'}}What a preset can and cannot do#
A preset only ever NARROWS. It is applied as a filter over the operators the column already offers — the per-type defaults on a client-mode grid, or the server-declared capability list on a resource-backed one. It can therefore never add an operator the server does not allow, and an unknown or unusable preset can never widen the offer either. This is the property that makes a custom preset safe to accept from configuration.
The consequence is worth stating plainly: a preset that names an operator the
column does not have available is a no-op for that operator, not an opt-in. See
with for the case where that matters.
Built-in presets#
| Name | Narrows to | Use for |
|---|---|---|
identity | equals, not | IDs, codes, enum values. |
search | contains | A column that behaves like a search bar. |
range | greater, less, between, greater, less | Statistics, years, measurements. |
comparison | equals plus the four comparisons | Numeric filters where exact match is also relevant. |
no | everything except is / is | Columns where "no value" is not a meaningful question. |
with | comparisons plus is / is | Nullable number/date columns — see the note below. |
with Empty#
is / is are deliberately absent from the default operator set
for number, date and boolean columns (the rationale is on get in
types/). with is the opt-in for the rare nullable column
where the question does make sense.
It only takes effect where the operator is already available, i.e. on a
resource-backed grid whose capability declares is for that column. On a
plain client-mode grid the base list does not contain is, and a preset
narrows rather than widens, so the preset changes nothing there. The same limit
applies to meta..
Use it only on columns whose underlying field is actually nullable (String?,
Int?, Date). On a non-nullable field the query builder in
@workspace/ does not build a null branch: a text column then
matches the empty string only, and for number, date and boolean the operator
allowlist rejects is before a condition is built at all. Declaring
nullable: true on a field that is not nullable is what validate
refuses.
Declaring your own presets#
<Grid may override a built-in preset and declare additional ones.
The map is merged per key with the built-ins, so adding one preset never
removes the others:
<GridDefaults filtering={{ operatorPresets: { // Override a built-in: every column that says `identity` follows this. identity: ['equals'], // Add one of your own. codeLookup: ['equals', 'startsWith'], }, }}> <App /></GridDefaults>{meta: {filterType: 'text', operatorPreset: 'codeLookup'}}The built-in names keep autocomplete on meta.; an additional
name is an ordinary string. The union of built-in names is exported as
Built for consumers writing shared column factories. Widening
that one field does not loosen the rest of the column meta — filter and
allowed stay closed unions.
There is no way to remove a built-in preset. Removing one would break the columns that name it.
How a bad preset behaves#
Every case is explicit, dev-warned, and fails towards "no narrowing":
| Situation | Result |
|---|---|
| The name is not a known preset | The column keeps its full operator list. A preset can only narrow, so an unknown name can never widen the offer. |
| An entry is not a filter operator (a typo) | That entry is dropped; the rest of the preset applies. |
| The same operator appears twice | Deduplicated, keeping the first position. |
| The preset is empty, or every entry was bad | Refused. An empty narrowing would leave the filter with nothing to choose. |
| The preset names an operator the server forbids | Intersected away. Narrowing cannot widen a capability. |
Warnings appear in development only (process.)
and are emitted once per column and reason.
Custom Operator Restriction#
For fine-grained control on a single column, use meta.:
{ meta: { filterType: 'number', allowedOperators: ['greaterThan', 'lessThan', 'between'], defaultFilterOperator: 'greaterThan', },}Enabling Operator Selection#
By default, filters use a single default operator. To show the operator selector dropdown:
{meta: {filterType: 'text', enableFilterOperators: true}}Default operators per type (when enable is false):
Text:
containsNumber:
equalsDate:
onDate Boolean:
equals
Date Filter Configuration#
Presentation and time zone#
The host supplies the reader's presentation through locale, including an
explicit time zone for relative dates and calendars. The grid does not resolve
application settings. Labels come from the host translator; date notation and
calendar locales delegate to @workspace/.
const presentation = {languageLocale: 'en-GB', timeZone: 'UTC'};
<ActiveGrid mode="client" gridId="measurements" locale={presentation} data={measurements} columns={columns}/>;Keep an object presentation stable across renders. The example chooses UTC explicitly; a real host supplies the time zone appropriate to its data view.
Presets#
DEFAULT_ includes today, yesterday, last 7 days, last 30 days,
this month, last month, this quarter and this year. Their get receives
the grid presentation. The relative day comes from its explicit time;
a missing zone throws a configuration error rather than using the browser's.
The returned Date carries calendar-day fields at local midnight. The date
filter reads those fields into yyyy- strings. These are calendar bounds,
not UTC timestamps. Do not convert them to instants before giving them back to
the date filter.
A column may supply its own meta., or false to hide the
preset list:
// Disable presets entirely — calendar only{meta: {filterType: 'date', dateFilterPresets: false}}A custom Date returns
{from: Date, to?: Date} and runs when selected. A fixed calendar range can be
concrete without assuming a time zone:
import type {DateFilterPreset} from '@workspace/active-grid';
const reviewPeriod: DateFilterPreset = { key: 'records.reviewPeriod', label: t('records.reviewPeriod'), type: 'range', getValue: () => ({ from: new Date(2026, 0, 1), to: new Date(2026, 2, 31), }),};For a relative custom preset, obtain the calendar day through the central
active-i18n date helpers using the explicit presentation zone, never from the
browser clock (new Date() plus date arithmetic reads the runtime's zone). The
host owns what the range means; the grid owns its filter representation.
Range Mode#
There are three ways to enable date range filtering:
1. Operator-driven range (recommended) — set
enable. The calendar switches to range mode
automatically when the user selects the in operator. All other
operators (on, before, after) show a single date picker.
// Operator dropdown controls the mode — user picks "In range" for range selection{meta: {filterType: 'date', enableFilterOperators: true}}2. Forced range mode — set date. The calendar is
always in range mode. Use for financial reporting or period selection where
you always want a date range, without exposing the operator dropdown.
// Always show range calendar — no operator selector needed{meta: {filterType: 'date', dateFilterMode: 'range'}}
// Range with date bounds{meta: {filterType: 'date', dateFilterMode: 'range', dateFilterMinDate: '2020-01-01', dateFilterMaxDate: '2026-12-31'}}Note: Do not combine
enablewithFilter Operators: true date— the forced range-mode overrides the operator-driven switching, making the operator-dropdown'sFilter Mode: 'range' in/single-date distinction ineffective. Use one or the other.Range
3. Toggle mode — set date. Shows a toggle that lets
the user switch between single-date and range mode.
Manual Date Entry#
The date filter includes a text input field above the calendar. Users type
the numeric date pattern of the grid's presentation (get,
active-i18n): dd- for nl, dd. for de, yyyy/ for
ja-, and so on for any locale the formatters accept. Every Unicode digit
set is read (٠٩-٠٣-٢٠٢٦ is 9 March 2026); no textual or localized date
grammar is accepted beyond that pattern.
The text input and calendar are bidirectionally synced. In range mode, two text inputs are shown ("from" and "to").
Calendar locale#
get delegates to active-i18n's composed
locale: language determines words and presentation preferences determine
notation. Active-grid has no separate language registry, and the calendar
refuses no locale the formatters accept:
month and weekday names come from that locale (derived from Intl for a tag without bundled date-fns wording), always in the Gregorian calendar;
the accessible labels (previous and next month, today, week) come from Intl (
get);Calendar Phrases day numbers are written in the locale's digits where the calendar library supports that numbering system;
the week starts where
Intl.week info says, and on Monday when the runtime has none.Locale
A tag the formatters refuse (malformed, without Intl data, or asking for a non-Gregorian calendar) throws active-i18n's UnsupportedLocaleError.
The calendar prop supplies a date-fns locale-object override for the
calendar. It does not replace the grid's value presentation or its explicit
time zone. If the object carries its own labels (a react-
import does), those labels win over the Intl-derived ones. For a custom
calendar, derive it from the grid's effective presentation:
import {useGridLocale} from '@workspace/active-grid';import {getDateFnsLocale} from '@workspace/active-format';
function CustomDateInput() { const calendarLocale = getDateFnsLocale(useGridLocale()); // Use calendarLocale in the custom calendar.}The built-in date filter performs this resolution itself. Date input parsing
supports the package's declared formats; a calendar library's wider range of
calendars, layouts or parsing conventions is not automatically a grid feature.
The internal owners are src/,
src/ and src/.
Fuzzy Search#
The global search (toolbar) uses a 5-level fuzzy matching algorithm:
| Level | Strategy | Example: searching "jhn" |
|---|---|---|
| 1 | Exact match | "jhn" matches "jhn" |
| 2 | Starts with | "jhn" matches "jhnson" |
| 3 | Word boundary | "jhn" would not match (no word starts with "jhn") |
| 4 | Contains | "jhn" would not match (not a substring) |
| 5 | Sequential characters | "jhn" matches "j- (j...h...n found in sequence) |
Fuzzy search is enabled by default. Disable via
Grid:
<GridDefaults behavior={{enableFuzzyFilter: false}}>Server-Side Filtering#
In server mode, filter values are passed directly to the fetch in
params.. The grid does not evaluate filters client-side — the
server is responsible for applying them.
Column filter values arrive as Filter objects (with operators) or as
arrays (for select filters). See server- for
implementation details.
A date filter sends calendar strings in value and, for a range, value.
The resource server validates and interprets those values according to the
server field's contract; a JavaScript Date from a preset is not forwarded
unchanged as a UTC timestamp. Calendar-day columns and timestamp columns have
different semantics: if a server query needs an instant interval, its server
owner derives the interval in the declared zone. Do not make the browser's zone
an implicit query owner.
Operator Labels#
Operator labels come from the host translator. The operator factories
(get, get, get
and the rest) take it as their argument; the static *_ arrays carry
English defaults and apply only when no translator is supplied. A supplied
translator is authoritative, including any raw key it returns. The factories,
the arrays and the label keys are listed in
../ § Operator Arrays + Factories.
Boolean Filter Variants#
The boolean filter supports two display modes.
Text variant (default)#
Three text buttons: All / Yes / No.
{meta: {filterType: 'boolean', booleanFilterVariant: 'text'}}Custom labels:
{ meta: { filterType: 'boolean', booleanFilterLabels: { all: 'Any', true: 'Enabled', false: 'Disabled', }, },}Icon variant#
Three icon buttons: — / ✓ / ✗.
{meta: {filterType: 'boolean', booleanFilterVariant: 'icon'}}