Filter

Composable filter builder for serializing field, condition, and value rows.

Quick Preview

Where

Usage

import {
  Filter,
  FilterApplyButton,
  FilterContent,
  FilterResetButton,
  type FilterFieldConfig,
} from "@giddaa-housing/ui/filter";

const fields: FilterFieldConfig[] = [
  { type: "text", field: "Name", label: "Name" },
  { type: "range", field: "Price", label: "Price" },
];

<Filter fields={fields} onApply={(serialized) => setFilter(serialized)}>
  <FilterContent />
  <FilterResetButton />
  <FilterApplyButton />
</Filter>

onApply receives the serialized JSON string (pass it straight to the API) plus the structured SerializedFilter[] if you prefer to build your own query. Use defaultValue to restore previously applied filters, e.g. from the URL.

Examples

Inline Filter Builder

Use this when products need a consistent field/condition/value filter interface.

Where

Trigger With Applied Count

Wrap your popover (or sheet) trigger in FilterTrigger to badge it with the number of applied filters. The badge updates on apply/reset, not while rows are being edited. Use the render prop instead for a fully custom trigger: render={({ count }) => <Button>Filters ({count})</Button>}.

Async Select Options

Give a select or multiSelect field a loadOptions function to fetch its options lazily. It is only called once the dropdown opens and the result is cached per field on the Filter, so wire it to fetch, a server action, or queryClient.fetchQuery — no data library is assumed.

Where

Custom Value Input

For full control (e.g. React Query with cache invalidation), replace a field's value input with renderValue and compose the exported FilterValueSelect / FilterValueText / FilterValueNumber / FilterValueDate / FilterValueBoolean / FilterValueRange components:

import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import {
  FilterValueSelect,
  type FilterFieldValueProps,
} from "@giddaa-housing/ui/filter";

function CityValueInput(props: FilterFieldValueProps) {
  const [open, setOpen] = useState(false);
  const { data, isLoading } = useQuery({ ...cityOptionsQuery, enabled: open });

  return (
    <FilterValueSelect
      {...props}
      options={data ?? []}
      loading={isLoading}
      open={open}
      onOpenChange={setOpen}
      placeholder="Select City"
    />
  );
}

const fields: FilterFieldConfig[] = [
  {
    type: "select",
    field: "Address.City",
    label: "City",
    renderValue: (props) => <CityValueInput {...props} />,
  },
];

Props

Filter

Prop/APITypeDefaultDescription
fieldsFilterFieldConfig[]-Defines allowed filter fields and input types.
onApply(serialized: string, filters: SerializedFilter[]) => void-Receives serialized filter JSON and the structured filters.
defaultJoin"AND" | "OR""AND"Connector used between generated filters.
defaultValuestring""Serialized filters used to initialize state.

FilterFieldConfig

Prop/APITypeDefaultDescription
fieldstring-Field id sent to the API; use dot notation for nested fields.
labelstring-Label shown in the field select.
type"text" | "number" | "date" | "select" | "multiSelect" | "range" | "boolean"-Picks the condition list and value input.
optionsFilterFieldOption[]-Static options for select fields; also shown while loadOptions is in flight.
loadOptions() => Promise<FilterFieldOption[]>-Lazily load options when the dropdown opens; cached per field.
renderValue(props: FilterFieldValueProps) => ReactNode-Replace the built-in value input for this field.

Other parts

Prop/APITypeDefaultDescription
FilterTrigger.render({ count }) => ReactNode-Render a custom trigger with the applied-filter count.
FilterTrigger.hideCountWhenZerobooleantrueHide the count badge while no filters are applied.
FilterContent.labelReactNode"Where"Heading above the rows.
FilterContent.addFilterLabelReactNode"Add Filter"Label of the add-row button.
FilterApplyButton.onApplySuccess() => void-Called after a valid apply, e.g. to close a popover.
FilterResetButton.onReset() => void-Called after filters are cleared.
parseSerializedFilters(value: string) => SerializedFilter[]-Parse an onApply string back into structured filters.

Accessibility

  • Keep validation messages visible and connected to controls.
  • Provide field labels that match product language.
  • Avoid exposing unsupported API filter conditions in fields.

On this page