Components Advanced
FilterBar
StableA responsive row of search, filters, results count and "Clear all".
FilterBar assembles the filtering controls above a table or list: an optional search field, any number of FilterChips, a "Clear all" action and a results count that screen readers hear when it changes. It wraps on narrow screens without breaking the page.
Overview
- Amara Okafor · Sales
- Lukas Novak · Engineering
- Priya Raman · Legal
- Hana Sato · Sales
- Oliver Grant · Finance
import * as React from 'react';
import { Button, FilterBar, FilterChip } from '@bindshq/stance-react';
const users = [
{ name: 'Amara Okafor', department: 'Sales' },
{ name: 'Lukas Novak', department: 'Engineering' },
{ name: 'Priya Raman', department: 'Legal' },
{ name: 'Hana Sato', department: 'Sales' },
{ name: 'Oliver Grant', department: 'Finance' },
];
export function Basic() {
const [query, setQuery] = React.useState('');
const [department, setDepartment] = React.useState<string | null>(null);
const results = users.filter(
(u) =>
u.name.toLowerCase().includes(query.toLowerCase()) &&
(!department || u.department === department),
);
return (
<div style={{ display: 'grid', gap: 16 }}>
<FilterBar
search={{
value: query,
onChange: setQuery,
label: 'Search users',
placeholder: 'Search by name',
}}
resultCount={results.length}
onClearAll={() => {
setQuery('');
setDepartment(null);
}}
canClear={Boolean(query || department)}
>
<FilterChip
label="Department"
value={department ?? undefined}
onClear={() => setDepartment(null)}
>
<div role="group" aria-label="Departments" style={{ display: 'grid', gap: 4 }}>
{['Sales', 'Engineering', 'Legal', 'Finance'].map((d) => (
<Button
key={d}
size="sm"
variant={department === d ? 'primary' : 'ghost'}
onClick={() => setDepartment(d)}
>
{d}
</Button>
))}
</div>
</FilterChip>
</FilterBar>
<ul style={{ margin: 0, paddingLeft: 20 }}>
{results.map((u) => (
<li key={u.name}>
{u.name} · {u.department}
</li>
))}
</ul>
</div>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
- Amara Okafor · Sales
- Lukas Novak · Engineering
- Priya Raman · Legal
- Hana Sato · Sales
- Oliver Grant · Finance
- Amara Okafor · Sales
- Lukas Novak · Engineering
- Priya Raman · Legal
- Hana Sato · Sales
- Oliver Grant · Finance
- Amara Okafor · Sales
- Lukas Novak · Engineering
- Priya Raman · Legal
- Hana Sato · Sales
- Oliver Grant · Finance
Import
import { FilterBar, FilterChip } from '@bindshq/stance-react';Platforms
FilterBar has native counterparts. They share Stance tokens and intent, and use each platform’s own controls and conventions. The live previews on this page are the web implementation.
import { FilterBar, FilterChip } from '@bindshq/stance-react';Examples
import * as React from 'react';
import { Button, FilterBar, FilterChip, Icon } from '@bindshq/stance-react';
export function WithActions() {
const [q, setQ] = React.useState('');
return (
<FilterBar
search={{ value: q, onChange: setQ, label: 'Search templates' }}
resultCount={24}
formatResultCount={(n) => `${n} templates`}
actions={
<Button size="sm" variant="outline" leadingIcon={<Icon name="arrows-up-down" />}>
Sort
</Button>
}
>
<FilterChip label="Owner" />
<FilterChip label="Updated" value="This month" onClear={() => {}} />
</FilterBar>
);
}import * as React from 'react';
import { FilterBar } from '@bindshq/stance-react';
export function SearchOnly() {
const [q, setQ] = React.useState('');
return (
<FilterBar
search={{
value: q,
onChange: setQ,
label: 'Search contracts',
placeholder: 'Contract or counterparty',
}}
/>
);
}API reference
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | None | FilterChips and other filter controls. |
search | { value: string; onChange: (v: string) => void; label?: string; placeholder?: string } | None | Optional search field. |
onClearAll | () => void | None | Shows "Clear all" when provided. |
canClear | boolean | true | Whether any filter is active. |
resultCount | number | None | Results count, announced politely on change. |
formatResultCount | (count: number) => string | "N results" | Formats the count. |
actions | ReactNode | None | Trailing content such as sort or view controls. |
label | string | 'Filters' | Accessible name of the group. |
Accessibility
- The bar is a labelled group ("Filters").
- The results count is a polite live region (role="status"), so screen readers announce new totals without moving focus.
- The search field is a native search input with an accessible label.
- Hide "Clear all" with canClear={false} when nothing is active, so it is not a dead control.
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves through search, each filter, its clear button and Clear all. |
Usage guidelines
Do
- Keep the most used filters visible; move rare ones into a "More filters" popover.
Don’t
- Announce counts on every keystroke for slow searches; debounce first.