KQ-1315 · FILTER/BUILDER

Filter Builder

A query builder over typed rows: rules of field, condition and value in nested All / Any groups, each with a rail down its left edge. Switching a group slides its thumb on snap, re-colours the rail and rolls every and between its rows to or on snap a cascade apart, so the change runs down the group; rules arrive from zero height on glide and leave on the exit ease while the gap closes on glide. Every change re-runs the query and the count rolls to its new figure a digit at a time on snap, chips above summarise the query and lead back to their rows, and the controls are native selects and inputs in groups and radio groups.

KQ-1315 · FILTER/BUILDER

Transactions

5 rules · 2 groups

151of 2,400 transactions

Rules without a value are left out of the count.

All of these, 2 rules
Any of these, level 2, 1 rule
All of these, level 3, 2 rules

151 of 2,400 transactions · 5 rules · 2 groups

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/filter-builder

Props

nesting
number
default 2
How deep groups may nest, 0 to 3. At 0 the query is a flat list of rules with no Add group and no inner rails.
chips
boolean
default true
A strip of chips above the builder summarising each top-level rule or group; a chip focuses its row and its × removes it.
count
"rows" | "share" | "off"
default "rows"
The result readout: a rolling row count, the share with a meter that glides to it, or none.
value / defaultValue
FilterGroup
default defaultFilterQuery
The query: a root group of rules and nested groups. Controlled or uncontrolled.
onValueChange
(query: FilterGroup) => void
default —
Fires from the control that changed the query, with the whole new query.
fields
FilterField[]
default defaultFilterFields
What can be filtered on: id, label, type (text, number, enum, date, boolean), enum options, number prefix and suffix.
rows
FilterRow[]
default defaultFilterRows
The records the count and preview are taken over. The default is 2,400 seeded transactions.
matches
number
default —
A count from your server; overrides counting rows.
total
number
default —
The total from your server. Defaults to rows.length.
now
number | Date
default defaultFilterNow
Where "in the last N days" counts back from.
onApply
(query: FilterGroup) => void
default —
The footer's Show button, and Enter in a value. Without it there is no Show button.
applyLabel
string
default "Show"
The Show button's verb; the count follows it.
maxRules
number
default 12
Rules allowed in the whole query; the Add buttons disable at the limit.
noun
{ one: string; other: string }
default { one: "row", other: "rows" }
What a row is called in the count and its announcement.
formatCount
(n: number) => string
default —
Counts as text. Defaults to grouped digits in en-US, so the server and the browser print the same.
renderRow
(row: FilterRow) => ReactNode
default —
One row of the desktop preview. Defaults to the first text field, a few others as meta, and the first number on the right.
status
"ready" | "loading" | "error"
default "ready"
Whether the count has arrived, for a server count: Counting… while loading, Count unavailable with Try again on error.
onRetry
() => void
default —
Try again was pressed while status is "error".
title
string
default "Filters"
The builder's heading; also its accessible name unless label is given.
label
string
default —
The builder's accessible name. Defaults to the title.
sound
boolean
default false
A click for each field, condition or match change, a pop for each rule or group added and a lower one for each removed. Typing is silent.
disabled
boolean
default false
Read only: the query shows and nothing changes.
className
string
default —
Classes for the root. It is at most 560px tall and its body scrolls between a fixed header and footer.