Press Filters: the rail opens beside the rows and stays there while you narrow, read, and narrow again. A panel that covers the answer has to be dismissed before each read; a rail does not.
- Welcome aboard
- Your invoice is ready
- Password changed
- Weekly digest
- A new device signed in
const [open, setOpen] = useState(false);
<Filter.Root
mode="live"
definitions={definitions}
values={values}
onValuesChange={setValues}
open={open}
onOpenChange={setOpen}
panelRenderedElsewhere
/>
<div className="flex gap-4">
<Table className="flex-1" />
<Filter.Dock
open={open}
onOpenChange={setOpen}
definitions={definitions}
values={values}
onValuesChange={setValues}
/>
</div>;panelRenderedElsewhere tells the bar to draw no editor of its own: its button
becomes a pressed/unpressed toggle for the rail, so the two never both draw
the fields. Under 768px the dock is the live Filter.Sheet
drawer instead — a rail would leave no room for the rows it filters.
API
| Prop | Type | Default | |
|---|---|---|---|
definitions | readonly FilterDefinition[] | required | The filters to edit, in the order they are rendered. |
onOpenChange | (open: boolean) => void | required | Called by the rail's own close button (and by the drawer on mobile). |
onValuesChange | (values: Readonly<Record<string, FilterValue>>) => void | required | Called as the filters change: at once for a tap, after a 300 ms pause for typing. Safe to use as a fetch key. |
open | boolean | required | Whether the rail shows. Controlled; the bar's trigger usually toggles it. |
values | Readonly<Record<string, FilterValue>> | required | The applied record. Changes made elsewhere (a removed chip) flow back in. |
labels | Partial<FilterLabels> | — | Overrides for the strings the rail shows, merged over the surrounding filter root's. The rail usually sits outside the bar that declared the copy, so pass the same labels to both. |
locale | string | — | BCP 47 tag used to format the numbers, money and dates; inherited when omitted. |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <aside> prop. Source: filter/components/filter-dock.tsx.
The rail is sticky so it follows a page-scrolled list down; in a
full-height layout whose table scrolls itself, pass
className="static h-full max-h-none". It usually sits outside the bar in
the tree, so give it the same labels and locale — or wrap both in one
Filter.Provider.