ComponentsData display

Filter bar

Structured filters as chips above any collection — tables, boards, calendars, galleries. “+ Filter” walks field → condition + value in one popover; every chip segment edits in place. Operators follow the field type, dates can be relative, options can load from the server, and the value is a plain FilterGroup you can put in a URL or a saved view.

Radix Popover + cmdk data-table/filter-bar.tsx, data-table/types.ts, data-table/filter-fns.tstheme · material · accent · density · font

88 of 400 deals match.

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { FilterBar, applyFilters, type FilterFieldDef, type FilterGroup } from "@vamos-ui/react/data-table";

const fields: FilterFieldDef[] = [
  { field: "stage", label: "Stage", type: "enum", options: stageOptions, quick: true },
  { field: "value", label: "Deal value", type: "currency" },
];
const [filters, setFilters] = React.useState<FilterGroup>({ logic: "and", clauses: [] });

<FilterBar fields={fields} value={filters} onChange={setFilters} resultCount={applyFilters(deals, filters, fields).length} />

Keyboard: the bar is one toolbar tab stop; ←/→, Home/End move between chip segments. In the popover type to search fields, Backspace on an empty input goes back a step, Enter applies, Esc cancels.

Matching is locale-aware (Turkish İ/ı folding) and between is inclusive; dates compare by calendar day in the provider’s time zone. A server must implement the same semantics (see filter-fns.ts).

Examples

Quick filters with counts (HR)

quick: true pins an empty dashed chip; facets show how many rows each option would keep.

120 people

Server-searched options

options can be (q) => Promise<FilterOption[]> — debounced, with a spinner, labels cached for the chips.

Relative dates

Presets and “Last N days/weeks/months”, resolved at query time so saved views stay current.

Resolves to Aug 13 – Nov 10, 2026 (today: Nov 10, 2026).

Above a board, synced to the URL

Not table-bound: encodeFilters / decodeFilters keep a standalone bar in ?board_f=…

Lead 25

Quarry AnalyticsSelin Kaya$3,563
Ridgeway LogisticsLeila Haddad$9,175
Halden WorksMarcus Webb$33,599
Fernhill MediaSelin Kaya$40,631

Qualified 18

Northwind TradersMarcus Webb$38,288
Halden WorksMarcus Webb$23,510
Basalt DynamicsMarcus Webb$4,932
Ege TekstilAda Park$27,158

Proposal 6

Lumora LabsTom Brennan$23,798
Brightmoor FoodsLeila Haddad$73,476
Halden WorksLeila Haddad$46,360
Halden WorksAda Park$2,802

Won 6

Brightmoor FoodsAda Park$41,078
Ridgeway LogisticsSelin Kaya$74,624
Pemberly ConfectionerySelin Kaya$13,284
Lumora LabsLeila Haddad$93,342

Read-only, sheet, collapsed, disabled

A shared-link preview without ×, the mobile sheet layout, maxVisibleChips and size sm.

Read-only (a shared link preview)

Sheet layout (automatic under 480px of width)

Collapsed chips · small · disabled

API

FilterBar

PropTypeDefault
fields
The filterable fields. From a Data Table: filterFieldsFromColumns(columns).
FilterFieldDef[]—
value / defaultValue / onChange
{ logic: "and" | "or", clauses } — controlled or uncontrolled. Incomplete clauses are never committed.
FilterGroup—
q / onQChange / searchPlaceholder
Shows a search box at the start when onQChange is set.
string · (q) => void—
facets
Option counts in the value list.
Record<field, Record<value, number>>—
resultCount
Announced after each change (“Filter added: Value at least $5,000. 84 results.”).
number—
maxVisibleChips
Collapse the rest into “+N”.
number—
layout
auto switches to a “Filters (n)” button + bottom Sheet under 480px of container width.
"auto" | "inline" | "sheet""auto"
size
Chip height (md follows --control-h).
"sm" | "md""md"
disabled / readOnly
readOnly hides × and the add button (shared-link previews).
boolean—
me
Adds “Me” to user fields, stored as the symbolic "$me".
{ id, name }—
addRequest
Open the editor for a field programmatically (Data Table “Filter by this column”).
{ field, key } | null—
actions
Extra controls at the end of the bar.
ReactNode—
labels
All strings, including operator phrases (op_in…) and the {field} {op} {value} sentence template for word order.
Partial<FilterBarLabels>—

FilterFieldDef

PropTypeDefault
field / label / type
text, number, currency, percent, date, datetime, enum, boolean, user, tags.
string · string · FieldType—
ops
Allowed operators. Defaults per type: text contains…, numbers eq/gt/between…, dates on/before/after/between/relative, enum/user/tags in/notIn/isEmpty.
FilterOp[]—
options
Static or server-searched options ({ value, label, icon?, tone?, keywords? }).
FilterOption[] | (q) => Promise<FilterOption[]>—
quick
Always visible as an empty dashed chip.
boolean—
multiple
Enum: false = single value (eq / neq).
booleantrue
group / icon / placeholder / currency
Picker section, icon, value placeholder, ISO currency.
string · Component · string · string—
accessor
applyFilters: read the value from a row (default row[field]).
(row) => unknown—

FilterChip

PropTypeDefault
clause / field
Field │ condition │ value │ × — each segment is a button with a full name.
FilterClause · FilterFieldDef—
onEdit / onRemove
Delete / Backspace on × also removes.
(step: "field" | "value", trigger) => void · () => void—
readOnly / disabled / size
Presentation states.
boolean · boolean · sm | md—