# How Plane's Work Item Filter and Query System Works in the Utils Package

> Explore Plane's work item filter and query system. Learn how its rich filter architecture transforms UI selections into efficient backend queries via configurable definitions and a store adapter for seamless state management.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: internals
- Published: 2026-06-18

---

**Plane's work item filter and query system utilizes a composable "rich-filter" architecture that converts UI selections into backend queries through configurable filter definitions and a store adapter that manages serialization between the MobX state, URL parameters, and API requests.**

Plane implements this system within the `@plane/utils` package of the `makeplane/plane` repository to provide a type-safe, extensible filtering mechanism for work items. The architecture separates filter definitions—which specify available operators, labels, and UI behaviors—from the state management logic that translates user selections into query objects. This design allows developers to register new filters by creating configuration files without modifying core state management code.

## Filter Configuration Architecture

Filter definitions reside in `packages/utils/src/work-item-filters/configs/filters/` and describe the metadata and behavior for each available filter. Each definition specifies a unique **filter key** (`id`) used in query objects, a human-readable **label**, an optional **icon** (`filterIcon`), a set of **supported operators** (e.g., `IN`, `EQ`), and a **UI configuration** dictating whether the filter renders as single-select or multi-select with search capabilities.

The core utility `createFilterConfig`, located in [`packages/utils/src/filter.ts`](https://github.com/makeplane/plane/blob/main/packages/utils/src/filter.ts), generates these definitions. This helper returns a higher-order function that accepts concrete parameters—such as available filter values—and produces a fully-typed filter configuration object. This approach ensures type safety across the filter key generics while maintaining flexibility for filter-specific options.

## Multi-Select UI Configuration

For filters requiring selection from predefined lists—such as State, State Group, or Assignee—the utils package exposes specialized helpers like `getStateMultiSelectConfig` and `getStateGroupMultiSelectConfig`. These functions construct a **`MultiSelectConfig`** object that defines how to render options, extract IDs and labels, and display icons.

The multi-select configuration integrates with the filter definition through the `supportedOperatorConfigsMap`, connecting collection operators to their corresponding UI components. This separation allows the same underlying data structures to power both the visual dropdown interface and the query generation logic.

## Operator Mapping and Constraints

Each filter configuration maps supported operators to specific UI behaviors via the `supportedOperatorConfigsMap` property. For example, the State filter exclusively supports the **`IN`** collection operator, which internally utilizes the **`EQUALITY_OPERATOR.EXACT`** single-value operator when processing multi-select values.

This mapping ensures that UI components only present valid operator choices while the adapter correctly translates complex collection operations into the primitive equality checks required by the backend API. The operator definitions are imported from `@plane/types` and provide the type constraints that govern filter interactions throughout the system.

## Store Adapter and Query Serialization

The `shared-state` package contains the **`workItemFiltersAdapter`** defined in [`packages/shared-state/src/utils/work-item-filters.helper.ts`](https://github.com/makeplane/plane/blob/main/packages/shared-state/src/utils/work-item-filters.helper.ts). This adapter serves as the bridge between the filter definitions and the MobX store, performing three critical functions:

- **Registration**: Imports and registers all filter configs exported from `@plane/utils` into the centralized store.
- **Serialization**: Converts active filter selections from the store into a **query object** format expected by the backend API (e.g., `{ state: ["open", "in-progress"] }`).
- **Deserialization**: Parses query parameters from the URL and hydrates the store's filter state, maintaining bidirectional synchronization between the UI and browser navigation.

When components need to access or modify filters, they invoke the **`useWorkItemFilters()`** hook located in [`apps/web/core/hooks/store/work-item-filters/use-work-item-filters.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/hooks/store/work-item-filters/use-work-item-filters.ts). This hook retrieves current selections from the store, processes them through the adapter, and returns a **`filterQuery`** object suitable for API client consumption.

## Implementing a Custom Filter

Extending the system requires creating a filter definition and registering it with the adapter. The following example demonstrates implementing a "Tag" filter:

```tsx
// packages/utils/src/work-item-filters/configs/filters/tag.ts
import { createFilterConfig, getMultiSelectConfig } from "@plane/utils";
import { COLLECTION_OPERATOR, EQUALITY_OPERATOR } from "@plane/types";

export type TCreateTagFilterParams = {
  tags: { id: string; name: string }[];
  filterIcon: JSX.Element;
};

export const getTagMultiSelectConfig = (params, singleValueOperator) =>
  getMultiSelectConfig(
    {
      items: params.tags,
      getId: t => t.id,
      getLabel: t => t.name,
      getValue: t => t.id,
      getIconData: () => null,
    },
    { singleValueOperator, ...params },
    params
  );

export const getTagFilterConfig = <P extends string>(key: P) =>
  (params: TCreateTagFilterParams) =>
    createFilterConfig<P>({
      id: key,
      label: "Tag",
      ...params,
      icon: params.filterIcon,
      supportedOperatorConfigsMap: new Map([
        [COLLECTION_OPERATOR.IN, (p) => getTagMultiSelectConfig(p, EQUALITY_OPERATOR.EXACT)],
      ]),
    });

```

After defining the configuration, register the filter in the shared-state adapter:

```tsx
// Registration within the adapter
import { getTagFilterConfig } from "@plane/utils/src/work-item-filters/configs/filters/tag";

workItemFiltersAdapter.register({
  key: "tag",
  configFactory: getTagFilterConfig("tag"),
});

```

Components consume the filter through the dedicated hook:

```tsx
import { useWorkItemFilters } from "@/hooks/store/work-item-filters/use-work-item-filters";

function WorkItemList() {
  const { filterQuery, setFilter } = useWorkItemFilters();

  // filterQuery contains { tag: ["frontend", "bug"] }
  const { data } = useQuery(["work-items", filterQuery], fetchWorkItems);

  return (
    <FilterDropdown
      filterKey="tag"
      onChange={value => setFilter("tag", value)}
    />
  );
}

```

## Key Implementation Files

The work item filter and query system spans several critical files within the repository:

- [`packages/utils/src/work-item-filters/index.ts`](https://github.com/makeplane/plane/blob/main/packages/utils/src/work-item-filters/index.ts) – Re-exports the complete filter configuration bundle.
- [`packages/utils/src/work-item-filters/configs/filters/state.ts`](https://github.com/makeplane/plane/blob/main/packages/utils/src/work-item-filters/configs/filters/state.ts) – Contains full filter definitions for State and State Group filters, including UI configurations and operator mappings.
- [`packages/utils/src/filter.ts`](https://github.com/makeplane/plane/blob/main/packages/utils/src/filter.ts) – Houses core utilities including `createFilterConfig` and `getMultiSelectConfig`.
- [`packages/shared-state/src/utils/work-item-filters.helper.ts`](https://github.com/makeplane/plane/blob/main/packages/shared-state/src/utils/work-item-filters.helper.ts) – Implements the `workItemFiltersAdapter` for store registration and query serialization.
- [`apps/web/core/hooks/store/work-item-filters/use-work-item-filters.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/hooks/store/work-item-filters/use-work-item-filters.ts) – Provides the `useWorkItemFilters` hook for component-level filter access.

## Summary

- **Filter definitions** in `packages/utils/src/work-item-filters/configs/` specify metadata, operators, and UI behavior using the `createFilterConfig` helper.
- **Multi-select helpers** like `getStateMultiSelectConfig` generate the configuration objects that power filter dropdown interfaces.
- **Operator mapping** through `supportedOperatorConfigsMap` connects collection operators (e.g., `IN`) to specific UI renderers and equality checks.
- **Store adapter** (`workItemFiltersAdapter`) registers filters, serializes state into API query objects, and deserializes URL parameters.
- **Hook interface** (`useWorkItemFilters`) exposes the current `filterQuery` and mutation methods to React components.
- The architecture is fully **type-safe** and **extensible**, requiring only a new config file and adapter registration to add filters.

## Frequently Asked Questions

### How do I add a new work item filter to Plane?

Create a new filter definition file in `packages/utils/src/work-item-filters/configs/filters/` using the `createFilterConfig` helper to specify the filter key, label, supported operators, and UI configuration. Export a configuration factory function, then register it with the `workItemFiltersAdapter` in [`packages/shared-state/src/utils/work-item-filters.helper.ts`](https://github.com/makeplane/plane/blob/main/packages/shared-state/src/utils/work-item-filters.helper.ts) using the `register()` method.

### What operators are supported in Plane's filter system?

The system supports collection operators such as `IN` (for multi-select scenarios) and equality operators like `EXACT` through the `COLLECTION_OPERATOR` and `EQUALITY_OPERATOR` enums from `@plane/types`. Each filter definition explicitly declares its supported operators via the `supportedOperatorConfigsMap`, which maps operators to their corresponding UI configuration functions.

### How does the filter state synchronize with the URL?

The `workItemFiltersAdapter` handles bidirectional synchronization between the MobX store and URL query parameters. When filter selections change in the store, the adapter serializes them into query string format and updates the browser URL. Conversely, when the page loads with query parameters, the adapter deserializes these values and hydrates the store state, ensuring the UI reflects the URL state.

### Where is the filter query object generated for API calls?

The query object is generated within the `useWorkItemFilters` hook located at [`apps/web/core/hooks/store/work-item-filters/use-work-item-filters.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/hooks/store/work-item-filters/use-work-item-filters.ts). This hook retrieves the current filter state from the MobX store, processes it through the `workItemFiltersAdapter` serialization logic, and returns a `filterQuery` object (e.g., `{ state: ["open"], priority: ["high"] }`) that API clients pass directly to backend endpoints.