API reference

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 type

Binds 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 of key → f.*().
  • options — the shared, drift-prone options only: arraySeparator, pagination.

Returns

PropertyDescription
configsThe bound config map, for reference (e.g. building UI metadata from it).
useFiltersThe hook with configs + shared options applied. Still takes hook-only options per call.
resolveFilterParamsThe 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 everywhere

Prefer 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.

On this page