composed · Composed pattern

FilterBar

@devalok/shilp-sutra/composed/filter-barView in Storybook
Live preview coming

Hand-curated previews ship in rolling waves. See it live in Storybook →

Reference

Exports

FilterBar, FilterSelect, FilterMultiSelect

Props

FilterBar

searchValue: string
onSearchChange: (value: string) => void (renders SearchInput when provided)
searchPlaceholder: string
onClearAll: () => void (renders "Clear all" button when provided)
size: "xs" | "sm" | "md" (propagated to all child controls via context)
children: ReactNode (FilterSelect / FilterMultiSelect controls)

FilterSelect

label: string
value: string
onValueChange: (value: string) => void
options: { value: string; label: string }[]
allLabel: string (label for the "all" option)

FilterMultiSelect

label: string
value: string[]
onValueChange: (values: string[]) => void
options: { value: string; label: string }[]

Defaults

size="sm", searchPlaceholder="Search...", allLabel="All"

Example

<FilterBar searchValue={search} onSearchChange={setSearch} onClearAll={clearFilters}>
  <FilterSelect
    label="Status"
    value={status}
    onValueChange={setStatus}
    options={[{ value: 'active', label: 'Active' }, { value: 'done', label: 'Done' }]}
  />
  <FilterMultiSelect
    label="Assignees"
    value={assignees}
    onValueChange={setAssignees}
    options={memberOptions}
  />
</FilterBar>

Composability

  • FilterBar + FilterSelect + FilterMultiSelect — three-part kit. FilterBar is the toolbar container; FilterSelect/FilterMultiSelect are the individual filter controls.
  • size propagates via context from FilterBar to every FilterSelect/FilterMultiSelect child. Don't set size on individual filters.
  • Children MUST be direct — the size cascade breaks if filters are wrapped in extra divs. Use React fragments or let them be direct children.
  • Active filter highlight: FilterSelect/FilterMultiSelect auto-show an accent border when their value is set (non-empty array for multi, non-"all" for single).
  • Pair with DataTable or any list: FilterBar sits above a DataTable/list; searchValue + onSearchChange drive global filter; individual filters drive column filters via your own state management.
  • "Clear all" convention: Passing onClearAll renders a Reset button that's your escape hatch — implement it to clear all filter state in one call.

Gotchas

  • FilterSelect and FilterMultiSelect must be direct children of FilterBar to inherit the size context
  • FilterSelect uses "all" as the sentinel value for "no filter" — do not use "all" as a real option value
  • Active filters get an accent border highlight automatically