The one filter row above every table: a growing search, dropdown chips at the end, clear, and the list's own action.
FilterBar lays out a list's filters in a fixed order: a FilterSearch that grows to fill the row, then Select chips (and a DateRangeFilter), then a ghost "Clear filters" while anything is applied, then the list's own action after a hairline ("New invoice").
It is presentational. Filter state lives with the screen, which also decides when to pass onClear (only while a filter is applied). No title, no "n of m shown" line and never a row of toggle chips.
Installation
npx shadcn@latest add KezLahd/rime/filter-barAlso installs: base, button.
Usage
import { FilterBar, FilterSearch, Select } from "@/components/ui";
<FilterBar aria-label="Filter invoices" onClear={filtering ? clear : undefined} actions={<Button size="sm" iconStart={<IconPlus size={14} />}>New invoice</Button>}>
<FilterSearch grow aria-label="Search invoices" value={query} onChange={setQuery} placeholder="Customer or invoice number" />
<Select appearance="chip" aria-label="Status" value={status} onChange={setStatus} active={status !== "all"} options={STATUSES} />
</FilterBar>Examples
Search, dropdowns, clear and action
API
Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.
FilterBar
| Prop | Type | Default | Description |
|---|---|---|---|
| childrenRequired | ReactNode | The filters, in order: a <FilterSearch grow /> first, then <Select appearance="chip" /> dropdowns. The search fills whatever the dropdowns leave, so the row always spans the table's full width. | |
| aria-labelRequired | string | Names the filter group for screen readers: "Filter seats". | |
| onClear | () => void | Shown as a ghost "Clear filters" after the dropdowns. Pass it only while a filter is applied. | |
| clearLabel | string | "Clear filters" | |
| actions | ReactNode | The list's own action, at the far end after a hairline: "Add team member". | |
| className | string |
FilterSearch
| Prop | Type | Default | Description |
|---|---|---|---|
| valueRequired | string | ||
| onChangeRequired | (value: string) => void | ||
| placeholder | string | "Search" | |
| aria-labelRequired | string | ||
| className | string | ||
| width | number | string | 220 | Width of the chip. Defaults to 220px. Ignored when grow is set. |
| grow | boolean | Fill the row: the search takes every pixel the other filters leave (FilterBar's pattern, where dropdowns sit at the end). |
FilterChip
| Prop | Type | Default | Description |
|---|---|---|---|
| active | boolean | false | Filter applied. Shown by tint and ink only, never a ring. |
| icon | ReactNode | ||
| count | number | ||
| childrenRequired | ReactNode |
Also accepts the native attributes of AriaAttributes, ButtonHTMLAttributes, DOMAttributes, HTMLAttributes.
Theme tokens
Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.
| Token | Default | Live value |
|---|---|---|
| --chip-height | var(--control-h-sm) | … |
| --chip-radius | var(--r-sm) | … |
| --filter-bar-gap | 14px | … |
System tokens it reads
| Token | Default | Live value |
|---|---|---|
| --brand-deep | tokens.css | … |
| --chip-active-fill | tokens.css | … |
| --chip-active-fill-hover | tokens.css | … |
| --chip-active-shadow | tokens.css | … |
| --control-h-sm | tokens.css | … |
| --density | tokens.css | … |
| --dur | tokens.css | … |
| --ease | tokens.css | … |
| --focus-color | tokens.css | … |
| --focus-ring | tokens.css | … |
| --font-body | tokens.css | … |
| --ink-body | tokens.css | … |
| --ink-brand | tokens.css | … |
| --ink-disabled | tokens.css | … |
| --ink-muted | tokens.css | … |
| --line-glass | tokens.css | … |
| --r-2xs | tokens.css | … |
| --r-full | tokens.css | … |
| --r-sm | tokens.css | … |
| --recess-fill | tokens.css | … |
| --recess-fill-hover | tokens.css | … |
| --rgb-brand | tokens.css | … |
| --rgb-contact | tokens.css | … |
| --rgb-white | tokens.css | … |
| --shadow-recess | tokens.css | … |
| --text-caption | tokens.css | … |
| --wash-active | tokens.css | … |
Accessibility
- The filters sit in role="group" named by the required aria-label ("Filter invoices").
- Every chip needs its own aria-label: chips show a value, not a label.
- Announce result counts from the table region (a polite status line), not from the bar.
Do and don't
Do
- Order: search, dropdowns, date, clear, then the action.
- Pass onClear only while a filter is applied.
- Use FilterSearch here, not SearchField, so the row's heights match.
Don't
- Never a row of toggle chips for a single-choice filter; use a Select chip.
- Don't put a heading or result count above the bar.
For AI agents
This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.
Open filter-bar.mdRelated: table, select, date-range-filter