defineFilters
Bind a screen's config and shared options once for the hook and the loader.
defineFilters(configs, options?) => {
configs,
useFilters,
resolveFilterParams
}
defineFilters<P>(configs, options?) // validate against an API typeBinds one screen's configs and the shared call option { arraySeparator, pagination }
so useFilters and resolveFilterParams can't drift. Available on every
createFilters instance and the default export. See
Sharing setup.
configs— a map ofkey → f.*().options— the shared, drift-prone options only:arraySeparator,pagination.
Returns
| Property | Description |
|---|---|
configs | The bound config map, for reference (e.g. building UI metadata from it). |
useFilters | The hook with configs + shared options applied. Still takes hook-only options per call. |
resolveFilterParams | The loader helper with configs + shared options applied. Takes only the raw search params. |
export const productFilters = defineFilters(
{
search: f.text({ label: 'Search' }),
brand: f.select({ label: 'Brand', valueType: 'string', options })
},
{ arraySeparator: '|' }
);
// component (hook-only options still allowed):
const { params, filters } = productFilters.useFilters({ defaultCommit: 'manual' });
// loader (same config + separator, guaranteed):
const params = productFilters.resolveFilterParams(new URL(request.url).searchParams);The per-call useFilters accepts the hook-only options (defaultCommit, meta,
history, shallow, clearOnDefault) — never arraySeparator / pagination,
which are fixed at the defineFilters call so the two sides stay identical.
Validating against an API type
Pass <P> to validate configs against your API's params type — the same
contract useFilters<P> enforces (required keys
declared, non-null params defaulted). Both the bound hook and the bound
loader then return params typed as exactly P:
export const productFilters = defineFilters<ProductListParams>({
status: f.select({ label: 'Status', valueType: 'string', options: statusOptions }),
sort: f.select({ label: 'Sort', valueType: 'string', options: sortOptions, defaultValue: 'date' })
});
// hook and loader both produce ProductListParams — checked once, aligned everywherePrefer the config checked but the inferred (most precise) params instead?
Validate with satisfies FiltersFor<P> and call defineFilters without the
type argument — see Typed params.
If you customized pagination or request.arrayFormat, bind FiltersFor to those
values first; unlike defineFilters<P>, the bare type doesn't know them.
It returns a bundle, not the config map
defineFilters hands back { configs, useFilters, resolveFilterParams }. Passing its result
straight into another useFilters(...) is a type error — reach for .configs, or call the
returned useFilters() / resolveFilterParams(search), which already have the config applied.