Guides

Building a toolbar

Render the filters array generically, with a reset button.

The filters array is a list of resolved filters ready to render. A toolbar is just a .map over it, switching on each filter's type:

/products
params
{
  "search": null,
  "status": null,
  "labels": null,
  "page": 1,
  "per_page": 10
}

Render by type

Each resolved filter carries its type, label, value, and onChange. A small switch covers the kinds you use:

function Control({ filter }: { filter: ResolvedFilter }) {
  switch (filter.type) {
    case 'text':
      return (
        <input
          placeholder={filter.label}
          value={filter.value ?? ''}
          onChange={(e) => filter.onChange(e.target.value || null)}
        />
      );
    case 'select':
      return (
        <select
          value={filter.value ?? ''}
          onChange={(e) => filter.onChange(e.target.value || null)}
        >
          <option value=''>{filter.label}</option>
          {filter.options.map((o) => (
            <option key={o.value} value={o.value}>
              {o.label}
            </option>
          ))}
        </select>
      );
    // ...one case per kind you use
    default:
      return null;
  }
}

TypeScript narrows filter inside each case — under case 'select', filter.options and the correct value type are available.

The toolbar

function Toolbar({ filters }: { filters: ResolvedFilter[] }) {
  return (
    <div className='toolbar'>
      {filters.map((filter) => (
        <Control key={filter.key} filter={filter} />
      ))}
    </div>
  );
}

A reset button

The hook's isFiltered is true when any visible filter is active. For a "Clear filters" button that should take effect immediately, use instantReset() — it clears every filter to its default in one write, bypassing commit modes:

const { filters, isFiltered, instantReset } = useFilters(configs);

{
  isFiltered && <button onClick={instantReset}>Clear filters</button>;
}

There's also reset(), which clears every filter but respects each one's commit mode — in a manual "Apply" sheet, wire the sheet's own "Reset" to reset() so clearing goes through the same Apply step as other changes. See Apply panel.

Per-filter state

Each resolved filter also exposes its own state (isDirty, committedValue, isFiltered, commit, …) and its own apply / cancel / reset — so a control never needs params passed in to know its status. See the ResolvedFilter reference for the full surface, and Apply panel for deferred-commit UI.

On this page