Ranges & Two Conditions
Filter one column by two conditions at once — between two numbers, inside a date span, or either of two values — joined by AND or OR. One setting on the column, and it works with your existing data source unchanged.
A range between two values
Salary between $70,000 and $100,000, and a review date inside the first quarter. Two conditions on one column, applied from code before the first request. Sort any column and the range still holds.
Both columns here open already ranged, applied from code before the first request. Press the funnel on
either one to see the two conditions behind the 2 badge, and change them.
Dates: before and after are strict
A number column has ≥ and ≤, so an inclusive range says exactly what it
means. A date column offers Equals, Before and After, and both comparisons
are strict — Before 31 March excludes 31 March itself. So a whole quarter is
written as the day either side of it: After 2023-12-31 AND Before 2024-04-01. Naming the
quarter's own first and last day would silently drop the rows that fall on them.
The empty range
Nothing stops you asking for ≥ 120000 AND ≤ 90000. The grid applies it and the empty state appears — a range that excludes itself is a real answer, not a broken grid.
Either of two values
The same pair of conditions with the join switched to OR — departments reading like Sales or like Support. Open the funnel on Department and toggle AND to watch the grid empty.
Two conditions do not have to be a range. Switch the join to OR and the same pair becomes "either of these" — here, departments reading like Sales or like Support. With AND this exact filter matches nothing, because no department is both.
Open the funnel on Department and press the AND toggle to watch the grid
empty, then switch back. The join is part of the entry your server receives, so it has to honour it —
see the joiner line in the previous example's server tab.
A range is two conditions on one column
A column filter holds one condition, so a number column gives you a floor or a ceiling and a date column gives you before or after. "Between 60k and 90k" and "started during Q1" need both ends at once — two conditions on the same column, joined by AND.
Set filter.multi: true and the column accepts a second condition. That is the whole
setup: two conditions is what a column allows by default, so there is nothing else to configure.
How it reads
- In
filterMode: 'row'the first condition edits inline in the filter row, and a small chip (with a+1badge) opens the second. - The popup shows one row per condition — pick an operator and a value — with an AND / OR toggle at the top.
- The join applies to the whole column. You choose it once, not per condition.
One condition or two — the entry changes shape
A column with multi: true does not always send a stack. With one condition applied the
entry is a plain { op, value }, exactly like a column without multi; the
{ kind: 'multi', join, conditions } shape appears from the second condition on. Both the
popup and the inline editor collapse a lone condition back down.
So read the shape, not the column definition: check isMultiFilter(entry) rather than
assuming every entry of a stackable column carries a list.
Client or server
The entry arrives on FetchParams.filters as plain, serialisable JSON. Pass the whole state
to filterStatePredicate(filters, columns) for an in-memory source — the same helper
resolves one-condition and two-condition entries — or translate it to SQL / Mongo so the filtering runs
over the full dataset. The server/employees.ts tab is one honest translation.
Range filter API
| Property | Type | Default | Description |
|---|---|---|---|
| column.filter.multi | boolean | false | Let the column hold more than one condition, joined by AND/OR. Required for a range — a range is two conditions on one column. |
| column.filter.maxConditions | number | 2 | How many conditions the column accepts. Two — a range — is the default and needs no configuration. |
| MultiFilterValue | { kind: 'multi', join: 'and' | 'or', conditions: FilterCondition[] } | — | The serialisable shape a ranged column contributes to params.filters — from the second condition on. |
| isMultiFilter(entry) | entry is MultiFilterValue | — | Type guard for the two shapes a stackable column can send. One condition collapses to { op, value }. |
| filterStatePredicate(filters, columns) | (row: T) => boolean | — | Library helper — resolves one-condition and two-condition entries into a single row predicate. |
| config.filterMode: 'row' | FilterMode | — | First condition edits inline; a chip with a +N badge opens the rest. |
| [initialState] | GridStateSnapshot | — | Applies a range before the first request — both examples here open filtered from code. |