Filters TypeScript helper for React
v1.0.0Added in 1.0.0
The filter model behind React filter builders and saved views: typed fields, AND / OR groups, evaluation, sentences and URL strings.
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.
Filters is a Pro React helper in the React hooks and utilities category, written in TypeScript for React 19. It installs as source with npx wingo-ui@latest add filters, together with Commerce, Date utils, Number format and Text match.
Add to a project
Requirements: React 19, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add filtersFilters is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import applyFilter from @/lib/filters.
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
viewdefault"ny-london"stringThe demo's saved view (a FilterView): customers in New York or London, overdue customers, large balances or invoices in the last 7 days.
combinatordefault"and""and" | "or"The root group's combinator. Nested groups keep their own.
localedefault"en-US"stringdescribeFilter 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
functionWhether 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
constantThe 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
constantThe operator a new condition starts with for each field type, e.g. contains for text and any-of for select.
DEFAULT_FILTER_OPERATOR_LABELS
constantEnglish 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
constantThe English glue of a described filter: and, or, the joiner inside lists, days and the operator words. Spread it to translate.
operatorsFor
functionThe operators a field offers: its own operators prop intersected with what its type allows.
operatorNeedsValue
functionWhether an operator takes a value; is-empty, is-true and the like do not.
operatorValueShape
functionWhich value input an operator needs for a field type: "none", "single", "list", "range" or "days".
createFilterGroup
functionA new AND or OR group with a fresh id. Call it in event handlers, not during render.
createFilterCondition
functionA 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
functionA fresh node id with an optional prefix, for nodes you build by hand.
isFilterGroup
functionType guard: whether a node is a group rather than a condition.
addFilterNode
functionAdds a condition or group to the root or to a nested group by id, returning a new tree.
updateFilterNode
functionPatches a condition or group by id anywhere in the tree, returning a new tree.
removeFilterNode
functionRemoves a node by id, returning a new tree; a nested group left empty is removed too.
findFilterNode
functionFinds a node by id anywhere in the tree, or null.
isConditionComplete
functionWhether a condition has every value its operator needs. Incomplete conditions are skipped, so a chip being built never empties the list.
flattenConditions
functionEvery complete condition in the tree as a flat list, optionally limited to known fields.
countActiveConditions
functionHow many complete conditions filter the rows, for a "Filters 3" badge.
describeCondition
functionThe 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
functionOne 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
functionA compact base64url string for ?filter= links and saved views. Node ids are not kept; they are made again on parse.
parseFilter
functionReads a serializeFilter string back into a group, or null for a broken or hand-edited link.
FilterField
typeA 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
typeA saved view: { id, name, filter, readOnly?, pinned? }.
FilterOperator
typeEvery 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.
Dependencies
Files
- lib/filters.ts
FAQ
Is the Filters helper free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Filters?
Run npx wingo-ui@latest add filters. It also adds the Wingo UI items it builds on (Commerce, Date utils, Number format and Text match). With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/filters.