Filter Bar
Toolbar above a table or list with a view switcher, a search field, filters and actions in one row that stacks on narrow screens.
Description
FilterBar is the row above a table, list, or card grid: a view switcher on one side, and search, filtering and actions on the other. Two props shape it.
variant picks the arrangement — segmented and tabs split the row in two, search lets the field flex across it, and selected is the tinted bulk-action bar shown once rows are checked. Below lg everything except selected stacks.
size is md (40px row) or sm (32px row) and cascades to every control inside: sizes, gaps, paddings and widths all follow it, so there is nothing else to keep in sync.
FilterBar is a Create UI Pro component. With a Pro seat, npx @create-ui/cli add filter-bar installs it and its dependencies, and the previews below are marked with a Pro badge.
Installation
Anatomy
Two kinds of parts.
Layout parts own the arrangement. FilterBarActions is the trailing cluster and becomes a column once the bar stacks; FilterBarGroup is a row that stays a row, keeping two controls side by side on a narrow screen. FilterBarResult is the applied-filter row — pass it as the last child and it renders below the bar, in the right gap, at the bar's size.
Sizing parts are thin shims over primitives you already have — FilterBarSearch is an InputGroup, FilterBarSelect a Select, FilterBarSegmented a SegmentedControl, and so on. Each injects what size and variant imply and spreads every other prop straight through, so the primitive keeps its full API.
Two variants change the skeleton. search has no trailing column, so FilterBarActions is not used:
selected draws its own chrome and never stacks:
Usage
Switching the whole bar to the 32px row is size="sm" and nothing else. To break one control out, pass size to that part alone — <FilterBarSearch size="md" />. For a shape the shims do not cover, drop the raw primitive in and size it yourself; they compose side by side.
Examples
Tab Menu
variant="tabs" when the switcher is a TabMenu. FilterBarTab takes a count and renders the badge for you, and a single FilterBarAction replaces the button group.
Search
variant="search" inverts the layout: the field flexes across the row and a FilterBarGroup trails it. FilterBarSearch reads the variant and flexes instead of taking its usual fixed width.
Selected
variant="selected" is the bulk-action bar shown once rows are checked. It brings its own tinted, outlined chrome and never stacks.
A FilterBarGroup className="w-auto" holds FilterBarCheckbox, the count, FilterBarSeparator and the FilterBarAction buttons, with FilterBarClear opposite. FilterBarAction reads the variant as well as the size: inside selected it renders ghost neutral-light, drops one step to fit the bar's own padding, and hides its label below lg. FilterBarClear is ghost and one step down already; the checkbox and separator just follow the size. Wire the checkbox and the count to your own selection state — the preview drives both from the list above it.
Filter result
FilterBarResult lists what is applied: a FilterBarResultLabel per facet, a FilterBarChip per value, and FilterBarClear. Pass it as the bar's last child — it is lifted out of the row and rendered below it, spaced by gap-component-lg:
Its padding and chips come from the bar's size — there is nothing to set twice. Pass open rather than conditionally rendering the row: it collapses and fades in both directions, then unmounts itself once the exit finishes. Omit open for an always-visible row. A bar with no Select (search, selected) has nothing to list, so it never renders one.
appearance picks whether the values sit on a band. filled (the default) draws bg-weak behind them; ghost drops it, and the chips switch to soft on their own so they still read as a set.
Sizes
The same bar at md and sm. One prop moves every nested control — the result row included — together.
Responsive
Below lg (1024px) everything drops onto its own row, except the pair inside a FilterBarGroup. The threshold is lg rather than md on purpose — a tablet renders exactly like a phone — and it is pure CSS, so there is no wrong first paint.
Accessibility
The bar and its layout parts are plain <div>s with no roles; semantics come from the controls inside, which the shims pass through untouched.
ARIA notes:
- Every icon-only control needs an
aria-label— aFilterBarSegmentin a view toggle, aniconOnlyFilterBarButton. FilterBarActionderives itsaria-labelfrom a string child, so a bulk-action button keeps its accessible name when the label is hidden on a narrow screen. Passaria-labelyourself when the child is not plain text.- Label
FilterBarCheckbox("Select all rows") and setchecked="indeterminate"for a partial selection. - The root is unlabelled by design. Wrap it in your own
<section aria-label="…">if a page has more than one bar.
Styling
Tailwind override: pass className to merge Tailwind classes with the component's classes (via cn()) on any part:
Data slots and attributes: the component sets these for CSS targeting.
data-slot="filter-bar"on the root; every part carries its own —filter-bar-actions,filter-bar-group,filter-bar-segmented,filter-bar-search,filter-bar-select,filter-bar-result, and so on.data-variantanddata-sizeon the root,data-appearance="filled" | "ghost"on the result row.
Related Components
- SegmentedControl / TabMenu — the switcher.
- InputGroup — the search field.
- Select — the filter dropdown.
- ButtonGroup / Button — the actions.
- Chip — the applied-filter values.
- Checkbox / Separator — the count control and divider inside
selected.
API Reference
FilterBar
The row, and the stack below lg. Provides size and variant to every part through context. Extends React.ComponentProps<"div">.
Props
Variants
FilterBarActions
The trailing cluster: a full-width column when stacked, an inline wrapping row from lg up. Not used by search. Extends React.ComponentProps<"div">.
FilterBarGroup
A row that stays a row — two controls side by side even when the bar stacks. Give each child min-w-0 flex-1 lg:flex-none so they split the stacked row; w-auto opts out of the full width, as selected does. Extends React.ComponentProps<"div">.
FilterBarResult
The applied-filter row. Pass it as the bar's last child; it renders below the row, in a gap-component-lg column, at the bar's size. Extends React.ComponentProps<"div">.
Props
Variants
Sizing parts
Each wraps one primitive, injects what size and variant imply, and spreads every other prop through — so the wrapped component's own props all work. Pass size on a container part to break a single control out of the bar's size. The item-level parts — FilterBarSegment, FilterBarTab, FilterBarButton — take no size of their own; they inherit it from the group around them, as their primitives do.
Types
FilterBarSize — "sm" | "md". FilterBarVariant — "segmented" | "tabs" | "search" | "selected". FilterBarProps, FilterBarResultProps — the parts' props. useFilterBar() reads the bar's size and variant. filterBarVariants, filterBarResultVariants — the raw cvas.
Built on this foundation
Every token on this page ships with the components, in code and in Figma.