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{
"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.