# Filters

> The filter model behind React filter builders and saved views: typed fields, AND / OR groups, evaluation, sentences and URL strings.

- Type: React helper (Hooks & Utilities)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add filters`
- Docs: https://wingo-ui.com/components/filters

## Interaction

Pick a saved view in the playground: its conditions show as chips, the sentence under them is describeFilter in the chosen locale, and the client list below is applyFilter over the twelve Northwind CRM customers. Switch the top-level combinator between AND and OR and watch the count change. Remove a chip with its × button (or Delete while it has focus) to see removeFilterNode; the url string at the bottom is serializeFilter and reads back with parseFilter.

## Installation

```bash
npx wingo-ui@latest add filters
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/filters` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `lib/filters.ts`

Wingo UI dependencies (installed automatically):

- [Commerce](https://wingo-ui.com/components/commerce) ([Markdown](https://wingo-ui.com/components/commerce.md))
- [Date utils](https://wingo-ui.com/components/date-utils) ([Markdown](https://wingo-ui.com/components/date-utils.md))
- [Number format](https://wingo-ui.com/components/number-format) ([Markdown](https://wingo-ui.com/components/number-format.md))
- [Text match](https://wingo-ui.com/components/text-match) ([Markdown](https://wingo-ui.com/components/text-match.md))

## Usage

```tsx
import { applyFilter, createFilterCondition, createFilterGroup, describeFilter, type FilterField } from "@/lib/filters"

type Client = { name: string; city: string; balance: number }
const fields: FilterField<Client>[] = [
  { id: "city", label: "City", type: "select", options: [{ value: "New York", label: "New York" }, { value: "London", label: "London" }] },
  { id: "balance", label: "Balance", type: "currency", currency: "USD" },
]

const filter = createFilterGroup("and", [
  createFilterCondition(fields[0], { value: ["New York", "London"] }),
  createFilterCondition(fields[1], { operator: "gte", value: 5000 }),
])

const rows: Client[] = [{ name: "Northwind Tech LLC", city: "New York", balance: 12480 }]
applyFilter(rows, filter, fields) // [Northwind Tech LLC]
describeFilter(filter, fields, { locale: "en-US" }) // "City is any of New York or London and Balance ≥ $5,000"
```

## API

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `view?` | `string` | `"ny-london"` | The demo's saved view (a FilterView): customers in New York or London, overdue customers, large balances or invoices in the last 7 days. |
| `combinator?` | `"and" \| "or"` | `"and"` | The root group's combinator. Nested groups keep their own. |
| `locale?` | `string` | `"en-US"` | describeFilter options.locale: money, numbers and dates in the sentence. The demo passes German operator labels for de-DE. |
| `applyFilter?` | `<T>(rows: T[], filter: FilterNode \| null, fields: FilterField<T>[], options?: { now?: Date \| number }) => T[]` |  | The main helper: the rows that pass the filter. Text ignores case and accents ("zurich" finds Zürich), dates compare local days, incomplete conditions and unknown fields are skipped, and an empty group passes everything. The same code filters on a server. |

## Parts

### evaluateFilter (function)

Whether one row passes a filter. Takes (row, filter, fields, { now? }); the same rules as applyFilter, for a single record or a server check.

### FILTER_OPERATORS (constant)

The operators each field type allows. Text: contains, not-contains, is, is-not, starts-with, ends-with. Numbers: eq, neq, gt, gte, lt, lte, between. Dates: on, before, after, between, in-last, in-next. Select: is, is-not, any-of, none-of. Multi-select: any-of, all-of, none-of. Boolean: is-true, is-false. Every type: is-empty, is-not-empty.

### DEFAULT_FILTER_OPERATOR (constant)

The operator a new condition starts with for each field type, e.g. contains for text and any-of for select.

### DEFAULT_FILTER_OPERATOR_LABELS (constant)

English words for every operator ("is any of", "does not contain", "in the last"). Pass your own through describeFilter's labels to translate.

### DEFAULT_FILTER_DESCRIBE_LABELS (constant)

The English glue of a described filter: and, or, the joiner inside lists, days and the operator words. Spread it to translate.

### operatorsFor (function)

The operators a field offers: its own operators prop intersected with what its type allows.

### operatorNeedsValue (function)

Whether an operator takes a value; is-empty, is-true and the like do not.

### operatorValueShape (function)

Which value input an operator needs for a field type: "none", "single", "list", "range" or "days".

### createFilterGroup (function)

A new AND or OR group with a fresh id. Call it in event handlers, not during render.

### createFilterCondition (function)

A new condition for a field with a fresh id and the type's default operator (30 days for in-last). Pass overrides for operator or value.

### createFilterId (function)

A fresh node id with an optional prefix, for nodes you build by hand.

### isFilterGroup (function)

Type guard: whether a node is a group rather than a condition.

### addFilterNode (function)

Adds a condition or group to the root or to a nested group by id, returning a new tree.

### updateFilterNode (function)

Patches a condition or group by id anywhere in the tree, returning a new tree.

### removeFilterNode (function)

Removes a node by id, returning a new tree; a nested group left empty is removed too.

### findFilterNode (function)

Finds a node by id anywhere in the tree, or null.

### isConditionComplete (function)

Whether a condition has every value its operator needs. Incomplete conditions are skipped, so a chip being built never empties the list.

### flattenConditions (function)

Every complete condition in the tree as a flat list, optionally limited to known fields.

### countActiveConditions (function)

How many complete conditions filter the rows, for a "Filters 3" badge.

### describeCondition (function)

The three parts of a chip: { field, operator, value }, e.g. "City", "is any of", "New York or London". Values are formatted in the locale.

### describeFilter (function)

One sentence for the whole filter: "City is any of New York or London and (Balance ≥ $12,000 or Last invoice in the last 7 days)". Pass locale and labels for another language.

### serializeFilter (function)

A compact base64url string for ?filter= links and saved views. Node ids are not kept; they are made again on parse.

### parseFilter (function)

Reads a serializeFilter string back into a group, or null for a broken or hand-edited link.

### FilterField (type)

A field people can filter by: { id, label, type, accessor?, options?, operators?, currency?, placeholder?, group? }. The value comes from row[id] unless accessor says otherwise.

### FilterCondition (type)

{ id, field, operator, value? }. Text takes a string, numbers a number or [min, max] with null for an open end, dates "2026-09-24" or [from, to], in-last a number of days, select a string or string[].

### FilterGroup (type)

{ id, combinator: "and" | "or", conditions }, where conditions mixes conditions and nested groups.

### FilterView (type)

A saved view: { id, name, filter, readOnly?, pinned? }.

### FilterOperator (type)

Every operator name, from contains to in-next.

## Examples

- **Search box**: One contains condition through applyFilter; case and accents never matter.
- **Shareable link**: serializeFilter for ?filter= URLs and parseFilter to read it back, with a safe null for broken links.
- **Operators by field type**: FILTER_OPERATORS with the English words from DEFAULT_FILTER_OPERATOR_LABELS.

## See also

- [Text match](https://wingo-ui.com/components/text-match) ([Markdown](https://wingo-ui.com/components/text-match.md))
- [Data Table](https://wingo-ui.com/components/data-table) ([Markdown](https://wingo-ui.com/components/data-table.md))
- [Toolbar](https://wingo-ui.com/components/toolbar) ([Markdown](https://wingo-ui.com/components/toolbar.md))

---

Canonical: https://wingo-ui.com/components/filters
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
