Skip to main content

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, including:

  • Installing the required dependencies
  • Wrapping your app with OsdkProvider2
  • Adding the CSS imports

Table of Contents

Import

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

Basic Usage

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

import { FilterList } from "@osdk/react-components/experimental/filter-list";
import { Employee } from "@YourApp/sdk";
import { $ } from "@YourApp/sdk";

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

Props Reference

Core Props

PropTypeRequiredDefaultDescription
objectSetObjectSet<Q>Yes-The object set to filter. Supports prefiltered sets (e.g. .where())
filterDefinitionsArray<FilterDefinitionUnion<Q>>No-Filter items to display. If omitted, all filterable properties shown

Filter Management

PropTypeDefaultDescription
filterClauseWhereClause<Q>-Current where clause (controlled mode)
onFilterClauseChanged(newClause) => void-Called when filter clause changes. Required in controlled mode
onFilterStateChanged(definition, newState) => void-Called when any filter's state changes
initialFilterStatesMap<string, FilterState>-Initial states for hydrating from external storage

UI Features

PropTypeDefaultDescription
titleReactNode-Title displayed in the filter list header
titleIconReact.ReactNode-Icon displayed next to the title
collapsedbooleanfalseWhether the panel is collapsed
onCollapsedChange(collapsed) => void-Called when collapsed state changes
showResetButtonbooleanfalseShow reset filters button in header
onReset() => void-Called when reset button is clicked
showActiveFilterCountbooleanfalseShow count of active filters in header
classNamestring-Additional CSS class name

Advanced

PropTypeDefaultDescription
addFilterMode"controlled" | "uncontrolled""uncontrolled"How filter visibility (add/remove) is managed. In uncontrolled mode, an "Add filter" popover is rendered. In controlled mode, the consumer manages visibility via filterDefinitions.
onFilterAdded(filterKey, definitions) => void-Called when a filter is added (shown). In uncontrolled mode, fires when a user selects a hidden filter from the "Add filter" popover.
onFilterRemoved(filterKey) => void-Called when a filter is removed (hidden). In uncontrolled mode, fires as a notification after the filter is hidden internally.
enableSortingbooleanfalseEnable drag-and-drop reordering of filters. Drag handles are rendered and filters can be reordered.
renderAddFilterButton() => React.ReactNode-Custom trigger for the add-filter button. In uncontrolled mode, customizes the popover trigger; in controlled mode, replaces the entire button area.

Filter Definitions

Filter Definition Types

TypeDescription
PROPERTYFilter on a direct property of the object type
KEYWORD_SEARCHFull-text keyword search across objects
CUSTOMCustom filter with user-provided render function
HAS_LINKToggle filter for whether a link exists
LINKED_PROPERTYFilter on a property of a linked object type

Property Filter Definition

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

FieldTypeDescription
keystringProperty key on the object type
labelstringDisplay label for the filter
filterComponentFilterComponentTypeWhich UI component to render (see table below)
filterStateFilterStateInitial state for the filter
isVisiblebooleanWhether the filter is initially visible (default: true)
colorMapRecord<string, string>Custom colors for LISTOGRAM bar values
listogramConfigListogramConfigConfiguration for LISTOGRAM display (see below)
renderValue(value: string) => stringCustom display and search text for filter values in dropdown items, chips, and listogram rows

Listogram Configuration

FieldTypeDefaultDescription
displayMode"full" | "count" | "minimal""full"full: bar + count, count: count only, minimal: label only
maxVisibleItemsnumber5Number of items shown before "View all" link appears

Filter Components

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

ComponentBest ForState Type
LISTOGRAMCategorical data with bar chartEXACT_MATCH
SINGLE_SELECTSingle value dropdownSELECT
MULTI_SELECTMulti-select dropdown with searchSELECT
TEXT_TAGSTag-based input with suggestionsEXACT_MATCH
CONTAINS_TEXTFree-text search on a propertyCONTAINS_TEXT
NUMBER_RANGEMin/max range slider for numbersNUMBER_RANGE
DATE_RANGEDate range pickerDATE_RANGE
TOGGLEBoolean on/off toggleTOGGLE
SINGLE_DATESingle date pickerSELECT
MULTI_DATEMultiple date pickerSELECT
TIMELINEDate range with start/endTIMELINE

Examples

Combining with ObjectTable

Use controlled filterClause to connect FilterList and ObjectTable:

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 { Employee } from "@YourApp/sdk";
import { $ } from "@YourApp/sdk";
import { useMemo, useState } from "react";

function EmployeeDashboard() {
const [filterClause, setFilterClause] = useState<
WhereClause<typeof Employee>
>({});
const objectSet = useMemo(() => $(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:

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={$(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:

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

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

<FilterList
objectSet={$(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:

import { FilterList } from "@osdk/react-components/experimental/filter-list";
import { Employee } from "@YourApp/sdk";
import { $ } from "@YourApp/sdk";
import { useMemo } from "react";

function EngineeringFilters() {
const engineeringSet = useMemo(
() => $(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:

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={$(Employee)} filterDefinitions={filterDefinitions} />;

Custom Listogram Colors

Assign colors to specific values in a listogram:

<FilterList
objectSet={$(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:

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

<FilterList
objectSet={$(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:

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

<FilterList
objectSet={$(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:

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

Collapsible Panel

Make the filter list collapsible:

import { useState } from "react";

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

return (
<FilterList
objectSet={$(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:

<FilterList
objectSet={$(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.

// Exclude mode is built into LISTOGRAM filters automatically.
// Users access it via the overflow menu (three dots) on each filter item.
<FilterList
objectSet={$(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.

@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:

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

For a full reference of CSS tokens, see the CSS Variables documentation.

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. $(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