v1.5

Filter Bar

Figma

Toolbar above a table or list with a view switcher, a search field, filters and actions in one row that stacks on narrow screens.

Pro
Status
Draft
Published

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

pnpm dlx @create-ui/cli add filter-bar

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.

<FilterBar>
  <FilterBarSegmented>{/* view switcher */}</FilterBarSegmented>
  <FilterBarActions>
    <FilterBarSearch />
    <FilterBarGroup>{/* filter + actions */}</FilterBarGroup>
  </FilterBarActions>
  <FilterBarResult>{/* applied-filter chips */}</FilterBarResult>
</FilterBar>

Two variants change the skeleton. search has no trailing column, so FilterBarActions is not used:

<FilterBar variant="search">
  <FilterBarSearch />
  <FilterBarGroup>{/* view toggle + action */}</FilterBarGroup>
</FilterBar>

selected draws its own chrome and never stacks:

<FilterBar variant="selected">
  <FilterBarGroup className="w-auto">
    {/* count + bulk actions */}
  </FilterBarGroup>
  <FilterBarClear>Clear selection</FilterBarClear>
</FilterBar>

Usage

import {
  FilterBar,
  FilterBarActions,
  FilterBarButton,
  FilterBarButtons,
  FilterBarGroup,
  FilterBarSearch,
  FilterBarSegment,
  FilterBarSegmented,
  FilterBarSelect,
} from "@/components/ui/filter-bar"
<FilterBar size="md" variant="segmented">
  <FilterBarSegmented value={view} onValueChange={setView}>
    <FilterBarSegment value="all">All</FilterBarSegment>
    <FilterBarSegment value="active">Active</FilterBarSegment>
  </FilterBarSegmented>
 
  <FilterBarActions>
    <FilterBarSearch placeholder="Search anything.." />
 
    <FilterBarGroup>
      <FilterBarSelect icon={<RiFilter3Fill />} label="Filter" count={2}>
        <Select.Item value="draft">Draft</Select.Item>
      </FilterBarSelect>
 
      <FilterBarButtons>
        <FilterBarButton leading={<RiDownloadLine />}>Export</FilterBarButton>
      </FilterBarButtons>
    </FilterBarGroup>
  </FilterBarActions>
</FilterBar>

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.

Pro

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.

Pro

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.

Pro
2 Selected

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:

<FilterBar variant="segmented">
  {/* switcher, search, filters */}
  <FilterBarResult open={hasFilters}>
    <FilterBarResultLabel>Status</FilterBarResultLabel>
    <FilterBarChip onClose={() => remove("draft")}>Draft</FilterBarChip>
    <FilterBarClear onClick={clearAll}>Clear all</FilterBarClear>
  </FilterBarResult>
</FilterBar>

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.

Pro
Department
Design
Development
Role
Product Designer
Department
Design
Development
Role
Product Designer

Sizes

The same bar at md and sm. One prop moves every nested control — the result row included — together.

Pro
Status
Draft
Published
Status
Draft
Published

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.

Pro

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.

KeyDescription
TabMoves focus across the switcher, search, filter and actions.
Space EnterActivates the focused segment, tab or button.
Enter SpaceOpens the filter select when its trigger is focused.
↑ ↓Moves through the open select's options.
EscapeCloses the open select and returns focus to the trigger.

ARIA notes:

  • Every icon-only control needs an aria-label — a FilterBarSegment in a view toggle, an iconOnly FilterBarButton.
  • FilterBarAction derives its aria-label from a string child, so a bulk-action button keeps its accessible name when the label is hidden on a narrow screen. Pass aria-label yourself when the child is not plain text.
  • Label FilterBarCheckbox ("Select all rows") and set checked="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:

<FilterBar className="mx-auto max-w-6xl" />

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-variant and data-size on the root, data-appearance="filled" | "ghost" on the result row.
[data-slot="filter-bar"][data-variant="selected"] {
  /* ... */
}
  • 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

PropTypeDefaultDescription
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-The switcher, plus FilterBarActions / FilterBarGroup.

Variants

VariantOptionsDefaultDescription
variant"segmented" "tabs" "search" "selected""segmented"The arrangement. Mirrored as data-variant.
size"md" "sm""md"Cascades to every part through context. Mirrored as data-size.
VariantLayout
segmentedSwitcher and actions pushed apart; component-lg stacked gap.
tabsSame split, component-sm stacked gap, min-h-11 wide row.
searchNo split — the field flexes across the row.
selectedTinted outlined pill, never stacks. Brings its own chrome.

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

PropTypeDefaultDescription
openboolean-Animates the row open and closed, unmounting after the exit. Omit for an always-visible row.
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-FilterBarResultLabels, FilterBarChips, and FilterBarClear.

Variants

VariantOptionsDefaultDescription
appearance"filled" "ghost""filled"filled draws a bg-weak band; ghost drops it. Mirrored as data-appearance.

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.

PartWraps
FilterBarSegmented / FilterBarSegmentSegmentedControl / its item
FilterBarTabs / FilterBarTabTabMenu / its item (count → Badge)
FilterBarSearchInputGroup + search icon + control
FilterBarSelectSelect (icon · label · placeholder · count)
FilterBarButtons / FilterBarButtonButtonGroup / its item
FilterBarAction / FilterBarClearButton
FilterBarCheckboxCheckbox
FilterBarSeparatorvertical Separator
FilterBarChipChip
FilterBarResultLabelthe muted facet label

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.

Pro Code Library

$39850% OFF
$199
lifetimesingle user