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/API | Type | Default | Description |
|---|---|---|---|
fields | FilterFieldConfig[] | - | 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. |
defaultValue | string | "" | Serialized filters used to initialize state. |
FilterFieldConfig
| Prop/API | Type | Default | Description |
|---|---|---|---|
field | string | - | Field id sent to the API; use dot notation for nested fields. |
label | string | - | Label shown in the field select. |
type | "text" | "number" | "date" | "select" | "multiSelect" | "range" | "boolean" | - | Picks the condition list and value input. |
options | FilterFieldOption[] | - | 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/API | Type | Default | Description |
|---|---|---|---|
FilterTrigger.render | ({ count }) => ReactNode | - | Render a custom trigger with the applied-filter count. |
FilterTrigger.hideCountWhenZero | boolean | true | Hide the count badge while no filters are applied. |
FilterContent.label | ReactNode | "Where" | Heading above the rows. |
FilterContent.addFilterLabel | ReactNode | "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.