# FilterList

A comprehensive guide for using the FilterList component from `@osdk/react-components`.

## Prerequisites

Before using FilterList, make sure you have completed the library setup described in the [README](https://github.com/palantir/osdk-ts/blob/main/packages/react-components/README.md#setup), including:

- Installing the required dependencies
- Wrapping your app with `OsdkProvider`
- Adding the CSS imports

## Table of Contents

- [Import](#import)
- [Basic Usage](#basic-usage)
- [Props Reference](#props-reference)
- [Filter Definitions](#filter-definitions)
- [Examples](#examples)
- [Styling](#styling)
- [Best Practices](#best-practices)

## Import

```typescript
import { FilterList } from "@osdk/react-components/experimental/filter-list";
```

## Basic Usage

:::note About `@my/osdk` and `./client`
`@my/osdk` is a placeholder for **your generated SDK package** (e.g. `@your-app/sdk`). `./client` is the file in your app where you exported the OSDK client returned by `createClient(...)`. Replace both with the actual paths in your project.
:::

The simplest way to use FilterList is with an objectSet and a few filter definitions:

```typescript
import { Employee } from "@my/osdk";
import { FilterList } from "@osdk/react-components/experimental/filter-list";
import client from "./client";

function EmployeeFilters() {
  return (
    <FilterList
      objectSet={client(Employee)}
      filterDefinitions={[
        {
          type: "PROPERTY",
          key: "department",
          filterComponent: "LISTOGRAM",
        },
        {
          type: "PROPERTY",
          key: "jobTitle",
          filterComponent: "LISTOGRAM",
        },
      ]}
    />
  );
}
```

## Props Reference

<!-- AUTOGEN:props START src=src/filter-list/FilterListApi.ts interface=FilterListProps — generated by scripts/gen-props.mjs; run `pnpm --filter @osdk/react-components gen-props` -->

Type parameters: `Q extends ObjectTypeDefinition`

| Name                       | Type                                                                                 | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| -------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `objectType`               | `Q`                                                                                  | **Required.** The object type definition for the objects being filtered. Used for metadata resolution (property types, display names).                                                                                                                                                                                                                                                                                                                          |
| `objectSet`                | `ObjectSet<Q>`                                                                       | Optional object set to scope aggregation queries. When omitted, aggregations run against the full object type.                                                                                                                                                                                                                                                                                                                                                  |
| `filterClause`             | `WhereClause<Q>`                                                                     | The current where clause to filter the objectSet. If provided, the filter clause is controlled. LINKED_PROPERTY filters are not included; use `onEffectiveObjectSet`.                                                                                                                                                                                                                                                                                           |
| `onFilterClauseChanged`    | `(newClause: WhereClause<Q>) => void`                                                | Called when the filter clause changes. Required in controlled mode.                                                                                                                                                                                                                                                                                                                                                                                             |
| `title`                    | `ReactNode`                                                                          | Optional title to display in the filter list header                                                                                                                                                                                                                                                                                                                                                                                                             |
| `titleIcon`                | `React.ReactNode`                                                                    | Optional icon to display next to the title                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `filterDefinitions`        | `Array<FilterDefinitionUnion<Q>>`                                                    | The definition for all supported filter items in the list If not supplied, all filterable properties will be available                                                                                                                                                                                                                                                                                                                                          |
| `onFilterStateChanged`     | `(definition: FilterDefinitionUnion<Q>, newState: FilterStateType) => void`          | Called when filter state changes                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `onEffectiveObjectSet`     | `(objectSet: ObjectSet<Q>) => void`                                                  | Called with the narrowed `ObjectSet` whenever filters change. Requires `objectSet` to be set.<br /><br />A linked filter only narrows the set when its definition has `reverseLinkName`. Linked filters without it are skipped here; read their state from `onFilterStateChanged` instead.                                                                                                                                                                      |
| `showFilteredOutValues`    | `boolean`                                                                            | When `true`, facets render greyed-out count=0 rows for values present in the unfiltered data but excluded by other active filters. Defaults to `false`.                                                                                                                                                                                                                                                                                                         |
| `addFilterMode`            | `"controlled" \| "uncontrolled"`                                                     | Controls how filter visibility (add/remove) is managed.<br /><br />- `"uncontrolled"` (default): FilterList manages visibility internally. An "Add filter" popover is rendered for filters with `isVisible: false`, and each visible filter shows a remove button. - `"controlled"`: The consumer manages which filters are visible via `filterDefinitions`. Filters with `isVisible: false` are excluded from the rendered list. Defaults to `"uncontrolled"`. |
| `onFilterAdded`            | `(filterKey: FilterKey<Q>, newDefinitions: Array<FilterDefinitionUnion<Q>>) => void` | Called when a filter is added (shown).<br /><br />In uncontrolled mode, this fires when a user selects a hidden filter from the "Add filter" popover.                                                                                                                                                                                                                                                                                                           |
| `onFilterRemoved`          | `(filterKey: FilterKey<Q>) => void`                                                  | Called when a filter is removed (hidden).<br /><br />In uncontrolled mode, this fires as a notification after the filter is hidden internally.                                                                                                                                                                                                                                                                                                                  |
| `onFilterVisibilityChange` | `(newStates: Array<{ filterKey: FilterKey<Q>; isVisible: boolean; }>) => void`       | Called when filter visibility or ordering changes, i.e. when filters are reordered, or (in uncontrolled mode) added or removed via the built-in show/remove controls.                                                                                                                                                                                                                                                                                           |
| `enableSorting`            | `boolean`                                                                            | Enable drag-and-drop reordering of filters. When true, drag handles are rendered and filters can be reordered. Reorder state is managed internally; consumers who need to track order should use controlled filterDefinitions.                                                                                                                                                                                                                                  |
| `collapsed`                | `boolean`                                                                            | Whether the filter list panel is collapsed                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `onCollapsedChange`        | `(collapsed: boolean) => void`                                                       | Called when the collapsed state changes                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `initialFilterStates`      | `Map<string, FilterStateType>`                                                       | Initial filter states for hydrating from external storage. These states are merged over definition defaults on mount. Use onFilterStateChanged to persist state changes externally.                                                                                                                                                                                                                                                                             |
| `showResetButton`          | `boolean`                                                                            | Show reset filters button in header                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `onReset`                  | `() => void`                                                                         | Called when reset button is clicked                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `showActiveFilterCount`    | `boolean`                                                                            | Show count of active filters in header                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `className`                | `string`                                                                             | Additional CSS class name                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `renderAddFilterButton`    | `() => React.ReactNode`                                                              | Custom render function for the "Add filter" button.<br /><br />- In uncontrolled mode: customizes the trigger element for the built-in add-filter popover. The popover behavior is handled automatically. - In controlled mode: replaces the entire add-filter button area. The consumer is responsible for all add-filter behavior.                                                                                                                            |

<!-- AUTOGEN:props END -->

## Filter Definitions

### Filter Definition Types

| Type              | Description                                         |
| ----------------- | --------------------------------------------------- |
| `PROPERTY`        | Filter on a direct property of the object type      |
| `KEYWORD_SEARCH`  | Full-text keyword search across objects             |
| `CUSTOM`          | Custom filter with user-provided render function    |
| `HAS_LINK`        | Toggle filter for whether a link exists             |
| `LINKED_PROPERTY` | Filter on a property of a linked object type        |
| `STATIC_VALUES`   | Filter on a fixed list of values (no OSDK fetching) |

Each filter definition type's fields are documented below.

### Property Filter Definition

When using `type: "PROPERTY"`, the definition supports:

<!-- AUTOGEN:props START src=src/filter-list/FilterListItemApi.ts interface=PropertyFilterDefinition — generated by scripts/gen-props.mjs; run `pnpm --filter @osdk/react-components gen-props` -->

Type parameters: `Q extends ObjectTypeDefinition`, `K extends PropertyKeys<Q> = PropertyKeys<Q>`, `C extends ValidComponentsForPropertyType<PropertyTypeFromKey<Q, K>> = ValidComponentsForPropertyType<PropertyTypeFromKey<Q, K>>`

| Name              | Type                                                                          | Description                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ----------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `searchField`     | `boolean`                                                                     | When `false`, the header monocle (search-values icon) is hidden even for filter components that ordinarily support in-filter search. Useful for `MULTI_SELECT`, which already has its own inline search field. Defaults to `true`.                                                                                                                                                                                            |
| `type`            | `"PROPERTY"`                                                                  | **Required.** Discriminator for filter definition type                                                                                                                                                                                                                                                                                                                                                                        |
| `id`              | `string`                                                                      | Optional unique identifier for stable keying across filter reorders.                                                                                                                                                                                                                                                                                                                                                          |
| `key`             | `K`                                                                           | **Required.** The property key to filter on                                                                                                                                                                                                                                                                                                                                                                                   |
| `label`           | `string`                                                                      | Display label for the filter                                                                                                                                                                                                                                                                                                                                                                                                  |
| `filterComponent` | `C`                                                                           | **Required.** The filter component type to render Must be compatible with the property type derived from the key, see ValidComponentsForPropertyType                                                                                                                                                                                                                                                                          |
| `filterState`     | `FilterStateByComponentType[C]`                                               | **Required.** The current state of the filter. If provided, the filter is controlled.                                                                                                                                                                                                                                                                                                                                         |
| `colorMap`        | `Record<string, string>`                                                      | Maps filter values to colors for visual differentiation. Used by LISTOGRAM (per-row bar colors).                                                                                                                                                                                                                                                                                                                              |
| `listogramConfig` | `{ displayMode?: "full" \| "count" \| "minimal"; maxVisibleItems?: number; }` | Configuration for LISTOGRAM display mode. Only applies when filterComponent is "LISTOGRAM".                                                                                                                                                                                                                                                                                                                                   |
| `renderValue`     | `(value: string) => ReactNode`                                                | Custom display function for filter values. Replaces the default string display in dropdown items, chips, and listogram rows. When the function returns a string, that string is also used for search matching within filter dropdowns. When it returns a non-string `ReactNode`, search falls back to the raw value.                                                                                                          |
| `showCount`       | `boolean`                                                                     | Show aggregation counts next to filter option values. Applies to LISTOGRAM, SINGLE_SELECT, and MULTI_SELECT components. Defaults to `true for LISTOGRAM and MULTI_SELECT, false for SINGLE_SELECT`.                                                                                                                                                                                                                           |
| `clickToFilter`   | `boolean`                                                                     | When true, clicking a bar in the histogram replaces the filter range with that bucket's `[min, max]`. Only applies to histogram-rendering filter components (`NUMBER_RANGE` and `DATE_RANGE`); ignored on other component types.<br /><br />Click replaces the current range — clicking a second bar discards the previous selection. Multi-bucket selection / shift+click union is NOT supported in v1. Defaults to `false`. |
| `isVisible`       | `boolean`                                                                     | Controls whether this filter is rendered. When false, the filter is hidden but its state is preserved. Defaults to `true`.                                                                                                                                                                                                                                                                                                    |

<!-- AUTOGEN:props END -->

#### Listogram Configuration

| Field             | Type                             | Default  | Description                                                     |
| ----------------- | -------------------------------- | -------- | --------------------------------------------------------------- |
| `displayMode`     | `"full" \| "count" \| "minimal"` | `"full"` | `full`: bar + count, `count`: count only, `minimal`: label only |
| `maxVisibleItems` | `number`                         | `5`      | Number of items shown before "View all" link appears            |

### Keyword Search Filter Definition

When using `type: "KEYWORD_SEARCH"`, the definition supports:

<!-- AUTOGEN:props START src=src/filter-list/types/KeywordSearchTypes.ts interface=KeywordSearchFilterDefinition — generated by scripts/gen-props.mjs; run `pnpm --filter @osdk/react-components gen-props` -->

Type parameters: `Q extends ObjectTypeDefinition`, `K extends StringPropertyKeys<Q> = StringPropertyKeys<Q>`

| Name                 | Type                       | Description                                                                                                                                                                                                                        |
| -------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `searchField`        | `boolean`                  | When `false`, the header monocle (search-values icon) is hidden even for filter components that ordinarily support in-filter search. Useful for `MULTI_SELECT`, which already has its own inline search field. Defaults to `true`. |
| `type`               | `"KEYWORD_SEARCH"`         | **Required.**                                                                                                                                                                                                                      |
| `id`                 | `string`                   | Optional unique identifier for stable keying across filter reorders.                                                                                                                                                               |
| `properties`         | `"all" \| K[]`             | **Required.** Properties to search within - "all": Search all string properties - K[]: Search specific string properties                                                                                                           |
| `label`              | `string`                   |                                                                                                                                                                                                                                    |
| `filterState`        | `KeywordSearchFilterState` | Controlled state for the filter. When provided, the filter becomes controlled and changes should be handled via onFilterStateChanged callback.                                                                                     |
| `defaultFilterState` | `KeywordSearchFilterState` | Default state for uncontrolled mode. Used when filterState is not provided.                                                                                                                                                        |
| `isVisible`          | `boolean`                  | Controls whether this filter is rendered. When false, the filter is hidden but its state is preserved. Defaults to `true`.                                                                                                         |

<!-- AUTOGEN:props END -->

### Has-Link Filter Definition

When using `type: "HAS_LINK"`, the definition supports:

<!-- AUTOGEN:props START src=src/filter-list/types/LinkedFilterTypes.ts interface=HasLinkFilterDefinition — generated by scripts/gen-props.mjs; run `pnpm --filter @osdk/react-components gen-props` -->

Type parameters: `Q extends ObjectTypeDefinition`, `L extends LinkNames<Q> = LinkNames<Q>`

| Name                 | Type                 | Description                                                                                                                                                                                                                        |
| -------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `searchField`        | `boolean`            | When `false`, the header monocle (search-values icon) is hidden even for filter components that ordinarily support in-filter search. Useful for `MULTI_SELECT`, which already has its own inline search field. Defaults to `true`. |
| `type`               | `"HAS_LINK"`         | **Required.**                                                                                                                                                                                                                      |
| `id`                 | `string`             | Optional unique identifier for stable keying across filter reorders.                                                                                                                                                               |
| `linkName`           | `L`                  | **Required.**                                                                                                                                                                                                                      |
| `label`              | `string`             |                                                                                                                                                                                                                                    |
| `filterState`        | `HasLinkFilterState` | **Required.**                                                                                                                                                                                                                      |
| `defaultFilterState` | `HasLinkFilterState` |                                                                                                                                                                                                                                    |
| `isVisible`          | `boolean`            | Controls whether this filter is rendered. When false, the filter is hidden but its state is preserved. Defaults to `true`.                                                                                                         |

<!-- AUTOGEN:props END -->

### Linked Property Filter Definition

When using `type: "LINKED_PROPERTY"`, the definition supports:

<!-- AUTOGEN:props START src=src/filter-list/types/LinkedFilterTypes.ts interface=LinkedPropertyFilterDefinition — generated by scripts/gen-props.mjs; run `pnpm --filter @osdk/react-components gen-props` -->

Type parameters: `Q extends ObjectTypeDefinition`, `L extends LinkNames<Q>`, `LinkedQ extends ObjectTypeDefinition = LinkedType<Q, L>`, `LinkedK extends PropertyKeys<LinkedQ> = PropertyKeys<LinkedQ>`, `LinkedC extends ValidComponentsForPropertyType<PropertyTypeFromKey<LinkedQ, LinkedK>> = ValidComponentsForPropertyType<PropertyTypeFromKey<LinkedQ, LinkedK>>`

| Name                       | Type                                                             | Description                                                                                                                                                                                                                                                                                                                                        |
| -------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `searchField`              | `boolean`                                                        | When `false`, the header monocle (search-values icon) is hidden even for filter components that ordinarily support in-filter search. Useful for `MULTI_SELECT`, which already has its own inline search field. Defaults to `true`.                                                                                                                 |
| `type`                     | `"LINKED_PROPERTY"`                                              | **Required.**                                                                                                                                                                                                                                                                                                                                      |
| `id`                       | `string`                                                         | Optional unique identifier for stable keying across filter reorders.                                                                                                                                                                                                                                                                               |
| `linkName`                 | `L`                                                              | **Required.**                                                                                                                                                                                                                                                                                                                                      |
| `reverseLinkName`          | `LinkNames<LinkedQ>`                                             | Set this to make the filter narrow `objectSet`; the result is emitted via `onEffectiveObjectSet`. The value names the link on the linked object type that points back to `Q` (the inverse of `linkName`).<br /><br />Leave unset to keep the filter UI-only. It still renders and fires `onFilterStateChanged`, but FilterList won't narrow on it. |
| `linkedPropertyKey`        | `LinkedK`                                                        | **Required.**                                                                                                                                                                                                                                                                                                                                      |
| `linkedFilterComponent`    | `LinkedC`                                                        | **Required.**                                                                                                                                                                                                                                                                                                                                      |
| `linkedFilterState`        | `FilterStateByComponentType[LinkedC]`                            | **Required.**                                                                                                                                                                                                                                                                                                                                      |
| `defaultLinkedFilterState` | `FilterStateByComponentType[LinkedC]`                            |                                                                                                                                                                                                                                                                                                                                                    |
| `filterState`              | `LinkedPropertyFilterState<FilterStateByComponentType[LinkedC]>` | **Required.**                                                                                                                                                                                                                                                                                                                                      |
| `label`                    | `string`                                                         |                                                                                                                                                                                                                                                                                                                                                    |
| `showCount`                | `boolean`                                                        | Show aggregation counts next to filter option values. Applies to LISTOGRAM, SINGLE_SELECT, and MULTI_SELECT linked components. Defaults to `true for LISTOGRAM and MULTI_SELECT, false for SINGLE_SELECT`.                                                                                                                                         |
| `renderValue`              | `(value: string) => ReactNode`                                   | Custom display function for filter values. Replaces the default string display in dropdown items, chips, and listogram rows. When the function returns a string, that string is also used for search matching within filter dropdowns. When it returns a non-string `ReactNode`, search falls back to the raw value.                               |
| `isVisible`                | `boolean`                                                        | Controls whether this filter is rendered. When false, the filter is hidden but its state is preserved. Defaults to `true`.                                                                                                                                                                                                                         |

<!-- AUTOGEN:props END -->

#### Two modes for LINKED_PROPERTY

**Auto-narrowing** — set `reverseLinkName`. FilterList composes `pivotTo(linkName).where(...).pivotTo(reverseLinkName)` and emits the narrowed set via `onEffectiveObjectSet`:

```typescript
{
  type: "LINKED_PROPERTY",
  linkName: "manager",          // Employee → Manager
  reverseLinkName: "directReports", // Manager → Employee (back-link)
  linkedPropertyKey: "fullName",
  linkedFilterComponent: "MULTI_SELECT",
  linkedFilterState: { type: "SELECT", selectedValues: [] },
  filterState: { type: "linkedProperty", linkedFilterState: { type: "SELECT", selectedValues: [] } },
}
```

**UI-only** — omit `reverseLinkName`. The filter still renders and fires `onFilterStateChanged`; downstream narrowing is up to the consumer:

```typescript
{
  type: "LINKED_PROPERTY",
  linkName: "manager",
  // no reverseLinkName — FilterList won't narrow objectSet on this filter
  linkedPropertyKey: "fullName",
  linkedFilterComponent: "MULTI_SELECT",
  linkedFilterState: { type: "SELECT", selectedValues: [] },
  filterState: { type: "linkedProperty", linkedFilterState: { type: "SELECT", selectedValues: [] } },
}
```

### Custom Filter Definition

When using `type: "CUSTOM"`, the definition supports:

<!-- AUTOGEN:props START src=src/filter-list/types/CustomRendererTypes.ts interface=CustomFilterDefinition — generated by scripts/gen-props.mjs; run `pnpm --filter @osdk/react-components gen-props` -->

Type parameters: `Q extends ObjectTypeDefinition`, `State extends BaseFilterState = CustomFilterState`

| Name                 | Type                                                             | Description                                                                                                                                                                                                                        |
| -------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `searchField`        | `boolean`                                                        | When `false`, the header monocle (search-values icon) is hidden even for filter components that ordinarily support in-filter search. Useful for `MULTI_SELECT`, which already has its own inline search field. Defaults to `true`. |
| `type`               | `"CUSTOM"`                                                       | **Required.**                                                                                                                                                                                                                      |
| `id`                 | `string`                                                         | Optional unique identifier for stable keying across filter reorders. If provided, takes precedence over `key` for state keying.                                                                                                    |
| `key`                | `string`                                                         | **Required.** Unique key for this custom filter                                                                                                                                                                                    |
| `label`              | `string`                                                         |                                                                                                                                                                                                                                    |
| `filterComponent`    | `"CUSTOM"`                                                       | **Required.**                                                                                                                                                                                                                      |
| `filterState`        | `State`                                                          | **Required.**                                                                                                                                                                                                                      |
| `defaultFilterState` | `State`                                                          |                                                                                                                                                                                                                                    |
| `renderInput`        | `(props: CustomFilterInputRendererProps<Q, State>) => ReactNode` | Render the input portion of the filter Used when rendering within a filter item wrapper                                                                                                                                            |
| `renderItem`         | `(props: CustomFilterItemRendererProps<Q, State>) => ReactNode`  | Render the complete filter item Used when you need full control over the item appearance                                                                                                                                           |
| `toWhereClause`      | `(state: State) => WhereClause<Q> \| undefined`                  | **Required.** Convert filter state to a WhereClause for filtering Required for the filter to affect the object set                                                                                                                 |
| `isVisible`          | `boolean`                                                        | Controls whether this filter is rendered. When false, the filter is hidden but its state is preserved. Defaults to `true`.                                                                                                         |

<!-- AUTOGEN:props END -->

### Static Values Filter Definition

When using `type: "STATIC_VALUES"`, the definition supports:

<!-- AUTOGEN:props START src=src/filter-list/types/StaticValuesTypes.ts interface=StaticValuesFilterDefinition — generated by scripts/gen-props.mjs; run `pnpm --filter @osdk/react-components gen-props` -->

Type parameters: `Q extends ObjectTypeDefinition`, `C extends StaticValuesComponentType = StaticValuesComponentType`

| Name              | Type                                                                          | Description                                                                                                                                                                                                                                                                                                          |
| ----------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `searchField`     | `boolean`                                                                     | When `false`, the header monocle (search-values icon) is hidden even for filter components that ordinarily support in-filter search. Useful for `MULTI_SELECT`, which already has its own inline search field. Defaults to `true`.                                                                                   |
| `type`            | `"STATIC_VALUES"`                                                             | **Required.**                                                                                                                                                                                                                                                                                                        |
| `id`              | `string`                                                                      | Optional unique identifier for stable keying across filter reorders. If provided, takes precedence over `key` for state keying.                                                                                                                                                                                      |
| `key`             | `string`                                                                      | **Required.** Key used for state management and auto WHERE clause generation. When `toWhereClause` is not provided, this is used as the property key in the generated WHERE clause.                                                                                                                                  |
| `label`           | `string`                                                                      | Display label for the filter                                                                                                                                                                                                                                                                                         |
| `filterComponent` | `C`                                                                           | **Required.** The filter component type to render                                                                                                                                                                                                                                                                    |
| `filterState`     | `FilterStateByComponentType[C]`                                               | **Required.** The current state of the filter                                                                                                                                                                                                                                                                        |
| `values`          | `string[]`                                                                    | **Required.** The static list of values to display in the filter component. These are rendered directly without OSDK aggregation.                                                                                                                                                                                    |
| `renderValue`     | `(value: string) => ReactNode`                                                | Custom display function for filter values. Replaces the default string display in dropdown items, chips, and listogram rows. When the function returns a string, that string is also used for search matching within filter dropdowns. When it returns a non-string `ReactNode`, search falls back to the raw value. |
| `showCount`       | `boolean`                                                                     | Show aggregation counts next to filter option values. Applies to LISTOGRAM, SINGLE_SELECT, and MULTI_SELECT components. Defaults to `true for LISTOGRAM and MULTI_SELECT, false for SINGLE_SELECT`.                                                                                                                  |
| `colorMap`        | `Record<string, string>`                                                      | Maps filter values to colors for visual differentiation. Used by LISTOGRAM (per-row bar colors).                                                                                                                                                                                                                     |
| `listogramConfig` | `{ displayMode?: "full" \| "count" \| "minimal"; maxVisibleItems?: number; }` | Configuration for LISTOGRAM display mode. Only applies when filterComponent is "LISTOGRAM".                                                                                                                                                                                                                          |
| `toWhereClause`   | `(state: FilterState) => WhereClause<Q> \| undefined`                         | Optional custom WHERE clause generator. When provided, this is used instead of auto-generating a WHERE clause from the `key` and filter state.                                                                                                                                                                       |
| `isVisible`       | `boolean`                                                                     | Controls whether this filter is rendered. When false, the filter is hidden but its state is preserved. Defaults to `true`.                                                                                                                                                                                           |

<!-- AUTOGEN:props END -->

### Filter Components

When using `type: "PROPERTY"` or `type: "LINKED_PROPERTY"`, specify a `filterComponent`:

| Component       | Best For                          | State Type      |
| --------------- | --------------------------------- | --------------- |
| `LISTOGRAM`     | Categorical data with bar chart   | `EXACT_MATCH`   |
| `SINGLE_SELECT` | Single value dropdown             | `SELECT`        |
| `MULTI_SELECT`  | Multi-select dropdown with search | `SELECT`        |
| `TEXT_TAGS`     | Tag-based input with suggestions  | `EXACT_MATCH`   |
| `CONTAINS_TEXT` | Free-text search on a property    | `CONTAINS_TEXT` |
| `NUMBER_RANGE`  | Min/max range slider for numbers  | `NUMBER_RANGE`  |
| `DATE_RANGE`    | Date range picker                 | `DATE_RANGE`    |
| `TOGGLE`        | Boolean on/off toggle             | `TOGGLE`        |
| `SINGLE_DATE`   | Single date picker                | `SELECT`        |
| `MULTI_DATE`    | Multiple date picker              | `SELECT`        |
| `TIMELINE`      | Date range with start/end         | `TIMELINE`      |

## Examples

### Combining with ObjectTable

Use controlled `filterClause` to connect FilterList and ObjectTable:

```typescript
import { Employee } from "@my/osdk";
import type { WhereClause } from "@osdk/api";
import { FilterList } from "@osdk/react-components/experimental/filter-list";
import { ObjectTable } from "@osdk/react-components/experimental/object-table";
import { useMemo, useState } from "react";
import client from "./client";

function EmployeeDashboard() {
  const [filterClause, setFilterClause] = useState<
    WhereClause<typeof Employee>
  >({});
  const objectSet = useMemo(() => client(Employee), []);

  return (
    <div style={{ display: "flex", gap: 16, height: 600 }}>
      <div style={{ width: 320, flexShrink: 0 }}>
        <FilterList
          objectSet={objectSet}
          filterDefinitions={[
            {
              type: "PROPERTY",
              key: "department",
              filterComponent: "LISTOGRAM",
            },
            { type: "PROPERTY", key: "team", filterComponent: "LISTOGRAM" },
            {
              type: "PROPERTY",
              key: "fullName",
              filterComponent: "CONTAINS_TEXT",
            },
          ]}
          title="Employee Filters"
          showResetButton={true}
          showActiveFilterCount={true}
          enableSorting={true}
          filterClause={filterClause}
          onFilterClauseChanged={setFilterClause}
        />
      </div>
      <div style={{ flex: 1 }}>
        <ObjectTable objectType={Employee} filter={filterClause} />
      </div>
    </div>
  );
}
```

### Add/Remove Filters (Uncontrolled Mode)

Use `addFilterMode="uncontrolled"` with `isVisible: false` on some filters to let users add and remove filters dynamically:

```typescript
const filterDefinitions = [
  {
    type: "PROPERTY",
    key: "department",
    label: "Department",
    filterComponent: "LISTOGRAM",
    filterState: { type: "EXACT_MATCH", values: [] },
  },
  {
    type: "PROPERTY",
    key: "team",
    label: "Team",
    filterComponent: "LISTOGRAM",
    filterState: { type: "EXACT_MATCH", values: [] },
  },
  {
    type: "PROPERTY",
    key: "fullName",
    label: "Full Name",
    filterComponent: "CONTAINS_TEXT",
    filterState: { type: "CONTAINS_TEXT" },
    isVisible: false,
  },
  {
    type: "PROPERTY",
    key: "startDate",
    label: "Start Date",
    filterComponent: "DATE_RANGE",
    filterState: { type: "DATE_RANGE" },
    isVisible: false,
  },
];

<FilterList
  objectSet={client(Employee)}
  filterDefinitions={filterDefinitions}
  addFilterMode="uncontrolled"
  showResetButton={true}
/>;
```

Filters marked `isVisible: false` appear in the "Add filter" popover. Users can add them to the list and remove visible filters via the close button. Reset restores the original visibility.

### Removable Filters (Controlled Mode)

When `addFilterMode="controlled"` and `onFilterRemoved` is provided, each filter shows a remove button on hover. The consumer manages the definitions array:

```typescript
const [definitions, setDefinitions] = useState(filterDefinitions);

const handleFilterRemoved = (filterKey) => {
  setDefinitions(prev => prev.filter(def => def.key !== filterKey));
};

<FilterList
  objectSet={client(Employee)}
  filterDefinitions={definitions}
  addFilterMode="controlled"
  onFilterRemoved={handleFilterRemoved}
  title="Removable Filters"
/>;
```

### Prefiltered ObjectSet

Pass a `.where()` objectSet to scope filter dropdown values. For example, to only show Engineering employees:

```typescript
import { Employee } from "@my/osdk";
import { FilterList } from "@osdk/react-components/experimental/filter-list";
import { useMemo } from "react";
import client from "./client";

function EngineeringFilters() {
  const engineeringSet = useMemo(
    () => client(Employee).where({ department: "Engineering" }),
    [],
  );

  return (
    <FilterList
      objectSet={engineeringSet}
      filterDefinitions={[
        { type: "PROPERTY", key: "jobTitle", filterComponent: "LISTOGRAM" },
        { type: "PROPERTY", key: "location", filterComponent: "LISTOGRAM" },
      ]}
    />
  );
}
```

The filter dropdowns will only show job titles and locations that exist within the Engineering department.

### Keyword Search Filter

Add a full-text search filter that searches across multiple properties:

```typescript
const filterDefinitions = [
  {
    type: "KEYWORD_SEARCH",
    properties: ["fullName", "department", "jobTitle", "locationCity"],
    label: "Search",
  },
  { type: "PROPERTY", key: "department", filterComponent: "LISTOGRAM" },
  { type: "PROPERTY", key: "locationCity", filterComponent: "LISTOGRAM" },
];

<FilterList
  objectSet={client(Employee)}
  filterDefinitions={filterDefinitions}
/>;
```

### Custom Listogram Colors

Assign colors to specific values in a listogram:

```typescript
<FilterList
  objectSet={client(Employee)}
  filterDefinitions={[
    {
      type: "PROPERTY",
      key: "department",
      filterComponent: "LISTOGRAM",
      colorMap: {
        Engineering: "#3b82f6",
        Marketing: "#f59e0b",
        Sales: "#10b981",
        Design: "#8b5cf6",
      },
    },
  ]}
/>;
```

### Custom Value Rendering

Use `renderValue` to customize how filter values are displayed and searched. The returned string replaces the raw value for both display and search matching. This is useful for showing human-readable names instead of IDs:

```typescript
const USER_NAMES: Record<string, string> = {
  "abc-123": "Alice Smith",
  "def-456": "Bob Jones",
};

<FilterList
  objectSet={client(Task)}
  filterDefinitions={[
    {
      type: "PROPERTY",
      key: "assigneeUserId",
      filterComponent: "LISTOGRAM",
      renderValue: (userId) => USER_NAMES[userId] ?? userId,
    },
  ]}
/>;
```

`renderValue` works with `LISTOGRAM`, `SINGLE_SELECT`, and `MULTI_SELECT` filter components. For `MULTI_SELECT`, it applies to both dropdown items and selected chips. Searching within a filter dropdown matches against the `renderValue` output.

For best performance, memoize `renderValue` with `useCallback` to avoid unnecessary re-renders:

### Listogram Display Modes

Control how much detail each listogram row shows:

```typescript
// "full" (default): checkbox + label + colored bar + count number
// "count": checkbox + label + count number (no bar)
// "minimal": checkbox + label only

<FilterList
  objectSet={client(Employee)}
  filterDefinitions={[
    {
      type: "PROPERTY",
      key: "department",
      filterComponent: "LISTOGRAM",
      listogramConfig: { displayMode: "count" },
    },
  ]}
/>;
```

### Limiting Visible Items

By default, LISTOGRAM filters show at most 5 items with a "View all" link. Override with `maxVisibleItems`:

```typescript
<FilterList
  objectSet={client(Employee)}
  filterDefinitions={[
    {
      type: "PROPERTY",
      key: "department",
      filterComponent: "LISTOGRAM",
      listogramConfig: { maxVisibleItems: 10 },
    },
  ]}
/>;
```

### Collapsible Panel

Make the filter list collapsible:

```typescript
import { useState } from "react";

function CollapsibleFilters() {
  const [collapsed, setCollapsed] = useState(false);

  return (
    <FilterList
      objectSet={client(Employee)}
      title="Filters"
      collapsed={collapsed}
      onCollapsedChange={setCollapsed}
      showActiveFilterCount={true}
      filterDefinitions={[
        { type: "PROPERTY", key: "department", filterComponent: "LISTOGRAM" },
      ]}
    />
  );
}
```

### Drag-and-Drop Sorting

Enable reordering of filters via drag and drop:

```typescript
<FilterList
  objectSet={client(Employee)}
  filterDefinitions={filterDefinitions}
  enableSorting={true}
/>;
```

### Exclude Toggle

LISTOGRAM and TEXT_TAGS filters support an exclude/include toggle. Hover a filter item and click the three-dot menu to toggle between "Keeping" and "Excluding" modes. When excluding, selected values are excluded from results and appear with a strikethrough.

```typescript
// Exclude mode is built into LISTOGRAM filters automatically.
// Users access it via the overflow menu (three dots) on each filter item.
<FilterList
  objectSet={client(Employee)}
  filterDefinitions={[
    {
      type: "PROPERTY",
      key: "department",
      filterComponent: "LISTOGRAM",
      filterState: { type: "EXACT_MATCH", values: [] },
    },
  ]}
/>;
```

## Styling

FilterList uses CSS custom properties included in `@osdk/react-components/styles.css` for theming. Override `--osdk-*` tokens to customize FilterList without affecting other components, or override `--bp-*` tokens for global theming.

```css
@layer osdk.styles, user.theme;

@import "@osdk/react-components/styles.css" layer(osdk.styles);

@layer user.theme {
  :root {
    --osdk-intent-primary-rest: #2563eb;
    --osdk-intent-primary-hover: #1d4ed8;
  }
}
```

Use the `className` prop for scoped styling:

```typescript
<FilterList
  objectSet={client(Employee)}
  className="my-custom-filters"
  filterDefinitions={[...]}
/>
```

For a full reference of CSS tokens, see the [CSS Variables documentation](./CSSVariables.md).

## Best Practices

- **Memoize filterDefinitions** -- define the array outside the component or wrap in `useMemo` to avoid unnecessary re-renders
- **Use controlled mode for persistence** -- provide `filterClause` and `onFilterClauseChanged` to persist filter state across navigation
- **Use objectSet constraints to scope filter values** -- pass a prefiltered objectSet (e.g. `client(Employee).where(...)`) so filter dropdowns only show relevant values
- **Keep filter lists focused** -- show 3-8 filters; too many filters overwhelm users
- **Use `addFilterMode="uncontrolled"` for progressive disclosure** -- start with a few visible filters and let users add more as needed
