Skip to content

FilterBar

The filters of a list: a search field, a toolbar of controls, the filters in force as removable chips and a way to clear them all. It is a GET form, so the filters live in the address.

import { FilterBar } from '@zitdevs/ui';

The demo is server HTML with no React on the page, which runs one script, enhanceFilterBars(document) from @zitdevs/ui/enhance. Change the kind and the form submits; press Clear all and the controls reset with focus back on the search field. Tab into the toolbar once and move between its controls with the arrow keys.

<FilterBar
id="people-filters"
label="Filter people"
action="/people"
search={{ id: 'q', label: 'Search people', defaultValue: q }}
clearHref="/people"
hiddenFields={{ sort: 'name:ascending' }}
activeFilters={[{ id: 'kind', label: 'Kind: Employee', removeHref: '/people' }]}
>
<Select id="kind" label="Kind" options={kinds} defaultValue={kind} />
</FilterBar>

The page reads the query, filters on the server and passes the filters in force back as activeFilters. Pair it with a DataTable whose sortFields carry the same filters.

Prop Type Default Notes
id string required The form’s id.
label string required Names the search region, such as “Filter people”.
action string required The page the filters apply to. The method is GET.
search { id, label, name?, placeholder?, defaultValue? } required The search field, outside the toolbar. name is the id by default.
children ReactNode — The filter controls, in a toolbar: Selects, buttons. Each needs a name to submit.
activeFilters ActiveFilter[] [] { id, label, removeHref }: the filters in force, each a chip that is a link.
clearHref string — The list with no filter. Renders the clear-all link.
hiddenFields Record<string, string> — What the form carries besides the filters, such as the sort. Kept when clearing.
linkAs LinkComponent 'a' Renders the chips and the clear link, such as Next’s Link.
labels Partial<FilterBarLabels> English { filters, activeFilters, apply, clearAll, remove }. es.filterBar from labels/es.
ref Ref<HTMLFormElement> — The form.
anything else <form> attributes —

Exported types: FilterBarProps, ActiveFilter, FilterBarLabels, and the label sets filterBarLabels. From @zitdevs/ui/enhance: enhanceFilterBars, FILTER_BAR_EVENT (zd:filterchange) and FilterBarChangeDetail.

  • The form is role="search", named by label. The search field sits outside the toolbar, so its caret keys (arrows, Home, End) keep working.
  • The controls are a role="toolbar": one tab stop, with Left and Right (wrapping), Home and End moving between them. On a native select those keys stand in for the select’s own; Up and Down still change its value.
  • Clear all returns focus to the search field, so a keyboard reader is not left on a link that went away. Every change is announced on the form as zd:filterchange.
  • Each chip is a link named “Remove filter: Kind: Employee”, and the chips are a labelled list.

The form submits with its Apply button, Clear all is a plain link to clearHref and each chip a link to the list without it, so every path works with no script. What the enhancer adds:

  • a change in a toolbar control submits the form;
  • Clear all resets the controls, submits through requestSubmit() and focuses the search field (a click with a modifier still opens the link);
  • the toolbar’s roving focus.

In React the form’s small client part runs the enhancer once it has hydrated. Without React, call enhanceFilterBars(document) once; it finds every form[data-filter-bar].

surface-raised and border-default for the chips, primary for the clear link, ink for text, the field’s roles for the search field and the focus ring. Nothing is hard-coded.