Filters
A filter bar of sentence-like chips with per-field operators and a two-step add menu
Composablebuilt onCombobox,Dropdown Menu,Popover,Scroll Area,Transition Panel,Text Morph,Tooltip,Kbd
The filter bar shows each active filter as a chip that reads like a sentence ("Status is In progress"). Use it on list and table views where people narrow results by several conditions at once.
For a single dropdown tied to a form field, use Combobox instead.
Preview
Installation
Usage
Composition
Features
- Picking a field in the add menu slides to its values, so a chip appears only once it has one.
- Searching the menu also matches values across fields: type "done" to add "Status is Done" in one step.
- Chips that outgrow the row scroll sideways, and the add and clear buttons stay in view.
- A chip still waiting on its value shows a dashed edge and doesn't count as active.
Examples
Field types
Use type to pick each field's value control.
| Type | Control | Value |
|---|---|---|
"select" | Searchable list, one pick | string | null |
"multiselect" | Searchable list, many picks | string[] |
"text" | Inline input | string |
"number" | Inline number input, two for between | number | null | NumberRange |
"custom" | Your control, rendered by renderValue | unknown |
Operators
Use operators to replace a field's operators or disabledOperators to hide some; each operator's shape picks its control.
| Shape | Control |
|---|---|
"scalar" | The field's normal control (the default) |
"range" | Paired min and max inputs |
"none" | No value, as in is empty (valueless: true for short) |
Multiselect
Use maxSelections to cap the picks and labels.selectedCount to word the "3 selected" summary.
Prefix and suffix
Use prefix and suffix on text and number fields for currency, units, or symbols.
Controlled
Pass value and onValueChange to own the filters; FilterActiveCount shows how many are active.
[]
Overflow
Chips scroll sideways once they outgrow the row; set overflow="wrap" to flow them onto new rows instead.
Sizes
Use size to set the height of every chip in the bar.
Variants
Use variant to pick the shell the chips and the add button sit on.
| Variant | Description |
|---|---|
outline | The default. An outline Button's fill and border. |
soft | A secondary Button's fill, for quieter toolbars. |
elevated | A raised surface, for bars inside Cards and Dialogs. |
A filter with no value yet shows a dashed border on every variant.
Keyboard shortcut
Set shortcut to a key that opens the add menu, ignored while typing or while a popup is open.
Press F (while not typing) to open the filter menu.
Empty state
Pass open and onOpenChange to FilterActionsAdd so a button outside the bar can open the add menu.
No filters yet
Narrow the list by status, title, or estimate.
Manual layout
Pass children to arrange the parts yourself.
URL state with nuqs
Wire value and onValueChange to nuqs to keep the filters in ?filters=, parsed back through asFilterValues.
Server-side filtering
onValueChange fires on every keystroke, so send only complete filters and let typing settle before you fetch.
State outside the bar
Use FiltersProvider and FiltersBar separately when UI outside the row (a results count, saved views) reads the same state through useFilters.
Accessibility
Keyboard
| Key | Where | Action |
|---|---|---|
Your shortcut | Anywhere but a text field | Opens the add menu |
Enter | Add menu | Picks the highlighted item, or adds a typed value |
Backspace | Empty add menu search | Goes back to the field list |
Escape | Add menu | Closes it and returns focus to the add button |
Backspace or Delete | A chip's remove button | Removes the filter and focuses the next chip |
| Arrow keys | Number input | Steps the value |
Screen readers
- Each chip is a group named after its filter, such as "Status is In progress", and each segment names itself.
- A hidden status region announces adds, removes, and clears, since focus lands elsewhere.
- The add menu flags an unreadable number with an "Enter a number" hint tied to the input.
Custom chips
Keep the filter-chip, filter-chip-remove, and filter-add slots on custom parts; keyboard removal and focus handoff look for them.
API Reference
Filters composes Cubby UI primitives and has no Base UI root of its own.
Props
Filters
FiltersProvider and FiltersBar in one. shortcut, overflow, and div props go to the bar; the rest go to the provider.
FilterAddButton
The add button and its menu. FilterActionsAdd is the same menu as a segment of FilterActions, and takes these props too.
FilterChip
Other parts
| Part | Notes |
|---|---|
FiltersProvider | Owns the state; takes the Filters props except shortcut and overflow. |
FiltersBar | The row. Takes shortcut, overflow, and children; with children, pass shortcut to the add part. |
FilterChips | Renders every chip, inside the scroll strip when overflow="scroll". |
FilterActions | The add and clear capsule. Pass children to add segments, styled with FILTER_SEGMENT classes. |
FilterActionsClear | The capsule's clear segment. children replace its icon. |
FilterClearButton | A standalone text clear button, hidden with no filters. |
FilterActiveCount | "3 active" text, counting complete filters, hidden at zero. |
FilterChipValue | Renders nothing for a valueless operator. |
Handlers you pass to a part (onClick, onKeyDown, onBlur) run first; call event.preventDefault() to skip the built-in behavior.
Hooks
| Hook | Returns |
|---|---|
useFilters() | Everything below; re-renders on every edit. |
useFiltersState() | filters. |
useFiltersActions() | Config and actions, stable while typing: addFilter, updateFilter, removeFilter, clearAll, getFilters, fields, fieldsById, labels, size, usedFieldIds. |
useFilterChip() | The current chip's filter, field, and size. |
Types
FilterField
A union keyed on type. Every field has id, label, and optional icon, operators, and disabledOperators.
| Type | Adds |
|---|---|
select, multiselect | options; multiselect also takes maxSelections |
text, number | prefix, suffix, placeholder; number also takes step |
custom | renderValue, and optional defaultValue and formatValue |
renderValue renders as its own component, so it can use hooks. formatValue turns the value into text for the chip's name; strings, numbers, and booleans work without it.
FilterValue
Helpers
| Helper | Does |
|---|---|
createFilter(field, partial?) | A new filter with an id, the first operator, and an empty value. |
patchFilter(field, filter, patch) | Applies a patch, resetting the value when the operator's shape changes. |
asFilterValues(json) | Parses untrusted JSON (a URL param) into filters. |
isFilterComplete(field, filter) | Whether the filter narrows anything yet. |
describeFilter(field, filter, names?) | "Status is In progress". |
formatFilterValue(field, filter) | The value as text, "In progress". |
operatorLabel(operator, names?) | An operator's name, translated by labels.operators. |
resolveOperators(field) | A field's operators after operators and disabledOperators. |
scalarOperatorFor(field) | The first operator that takes one value. |
withOption(field, value, option) | Adds an option to a multiselect value, or replaces any other. |