A filter bar for a table: Filter widens into the filter panel at the bar's own width, fields beside their options, a search widens across the bar, and the filters applied sit under it as chips.
Installation
When to use
- Above a table or a list with a few fields to narrow it by (status, plan, owner): the filters applied stay in sight as chips, one click from changing.
- When a field has many options: a long list gets its own search, and each option can say how many rows it would leave.
When not to use
- One or two filters: a Segmented Control or a Select beside the table is quicker.
- Filters that need typed ranges or dates: a form fits them better.
Usage
Filter Bar with your own data: a whole component you can paste as a page.
"use client"
import { FilterBar } from "@/components/dashral/pro/filter-bar"
import { useState } from "react"
const fields = [
{
id: "status",
label: "Status",
options: [
{ value: "active", label: "Active" },
{ value: "trial", label: "Trial" },
{ value: "churned", label: "Churned" },
],
},
{
id: "plan",
label: "Plan",
multiple: false,
options: [
{ value: "starter", label: "Starter" },
{ value: "team", label: "Team" },
],
},
]
export default function Customers() {
const [filters, setFilters] = useState<Record<string, string[]>>({ status: ["active"] })
const [query, setQuery] = useState("")
return <FilterBar fields={fields} value={filters} onValueChange={setFilters} search={{ value: query, onValueChange: setQuery }} />
}API reference
FilterBar
A filter bar whose button widens into the filter panel, at the bar's own width: fields beside their options, a gliding highlight, full keyboard. The filters applied show as chips; a chip opens its own field, × or Backspace removes it.
| Prop | Type | Default |
|---|---|---|
fields | FilterField[]Required. | — |
value | FilterValue | — |
defaultValue | FilterValue | — |
onValueChange | (value: FilterValue) => void | — |
label | stringThe button's words; "Filter" by default. | "Filter" |
results | numberThe rows the filters leave, for the panel's button ("Show 24 results"); without it the button says Done. | — |
search | FilterBarSearchA search across the bar: its button widens into a field at the bar's width, the rows filtering as you type (the table stays in sight). Closed, what was typed stays as a chip. | — |
end | React.ReactNodeAnything at the bar's end: a view switch, an export button. It steps back with the bar. | — |
className | string | — |
classNames | FilterBarClassNames | — |
type FilterOption
| Prop | Type | Default |
|---|---|---|
value | string | — |
label | string | — |
icon | React.ReactNodeA small mark before it: a dot, an avatar, an icon. | — |
count | numberHow many rows it would show, beside it. | — |
type FilterField
| Prop | Type | Default |
|---|---|---|
id | string | — |
label | string | — |
icon | React.ReactNode | — |
options | FilterOption[] | — |
multiple | booleanSeveral options at once (the default), or one. | — |
searchable | booleanA search over its options; on by default past 6 options. | — |
type FilterValue
The options picked, by field id. A field with none is left out.
= Record<string, string[]>type FilterBarClassNames
| Prop | Type | Default |
|---|---|---|
root | string | — |
bar | string | — |
chip | string | — |
panel | string | — |
type FilterBarSearch
| Prop | Type | Default |
|---|---|---|
value | string | — |
defaultValue | string | — |
onValueChange | (query: string) => void | — |
placeholder | string"Search…" by default. | — |
Keyboard
| Keys | Action |
|---|---|
| ←or→ | Along the bar: Filter, each chip, Clear, Search and your buttons |
| Enter | On Filter: open the panel; on a chip: open it on that chip's field |
| BackspaceorDelete | On a chip: remove that filter |
| / | Open the search, from anywhere on the page (with search) |
| ↑or↓ | Through the fields, or a field's options |
| HomeorEnd | The first or last field or option |
| →orEnter | From a field into its options |
| ← | From the options back to the fields |
| SpaceorEnter | Tick an option |
| Esc | Back to the fields on a narrow bar, or close; the focus returns where it opened |
Accessibility
- The bar is a toolbar named Filters; each chip reads its filter and how to remove it ("Status: Active, Trial. Press Backspace to remove").
- The panel is a dialog (aria-modal): the fields are a vertical tablist, a field's options a listbox (multi-select where multiple) with the highlighted option as its active descendant.
- The search's result count is a live region; the bar's own content is hidden from screen readers while the panel or the search is open.
Motion
- Filter, a chip or the search button widens to the bar's width (0.42 s) while the bar's content steps back, and shrinks back onto where it came from, scrolling it into view first.
- Options slide up or down as the field changes and the panel reshapes; chips fade in and out while the others slide over, and the bar's height glides.
- Smooth tweens on [0.4, 0, 0.2, 1], never springs. With reduced motion it only fades.