Skip to main content
This guide covers changes to the beta FilterBar component. These APIs may continue to evolve before the stable release.

Overview

Custom filters (type: "custom") can now work and look like the built-in filter types:
  • emptyValue - The value that means “no filter”. Clear Filters and the drawer count respect it.
  • isEqual - How the filter compares values. Defaults to a structural comparison.
  • formatLabel - Trigger content while the filter is active.
  • keepOpenOnChange - Keeps the popover open between changes when controlledFiltering is off.
  • clear - Adds a footer Clear button to the popover.
  • commitOnSelect - Commits each popover change and closes.
  • FilterRenderProps - Adds committedValue, isDraft, commit, close, and filter.
  • icon - A leading trigger icon on a custom filter. It replaces the chevron. The drawer does not show it.
A filter this library does not ship is a contribution back to the library. Two custom filter bugs are also fixed. Editing a string-array value no longer throws, and Apply no longer drops edits to fields other than id. The popover for custom, text, and numeric range filters also handles fields with their own menu, such as a SelectField:
  • Pressing a label or other non-focusable spot inside the popover no longer closes it.
  • Escape and Enter go to an open menu first. Escape then closes the popover without clearing a SelectField inside it.
  • With controlledFiltering, pressing Enter to apply no longer reopens the popover.
Several custom filter defaults change, so existing custom filters can behave differently. See Breaking Changes.

Design Rationale

Why compare values structurally?

The popover compared custom values the way it compares select options. Objects with an id matched by id alone, and arrays of those objects matched by their set of ids. Edits to any other field looked unchanged, so Apply ignored them. Arrays of strings threw an error.
Consumers define custom values, so the default now compares every field. A filter whose values are identified by id can say so with isEqual.

Why emptyValue?

A custom filter was active whenever its value wasn’t undefined. A filter that clears to [] stayed active, kept Clear Filters visible, and counted toward the drawer badge. emptyValue names the value that means “no filter”, and Clear Filters writes it back.

Why formatLabel?

Showing the selection on the trigger meant rewriting label in onFilterChange. formatLabel derives the trigger content from the committed value, as the built-in filters do.

Why icon on a custom filter?

A custom trigger showed only a chevron. icon lets that trigger show what the filter is for, such as a place for a nearby filter. The drawer doesn’t show the icon, because each drawer cell already has a visible label.

Reference

Filter render props

buttonRender and drawerRender receive the same props: With commitOnSelect, the popover has no draft: value is the committed value and isDraft is false.

Migration Guide

Trigger labels

Move label logic out of onFilterChange and into formatLabel.

Empty values

Set emptyValue instead of mapping an empty value to undefined.

Id-only comparison

Pass isEqual when a value is identified by id and other fields shouldn’t count as edits.

Drawer items compared by reference

The drawer edits a copy of the committed value, including nested arrays, plain objects, and Dates. Compare items by a key.

Tests that query the drawer cell by label

Each custom drawer cell is a group named by the filter’s label. A label query for the same text now matches both the group and the control inside it, so query the control by role.

Calling render functions directly

Code that calls buttonRender or drawerRender itself, such as a unit test, must pass the new required props.

Breaking Changes

These changes require code updates before upgrading. The previous behavior is no longer supported.
The following changes are breaking:
  • Custom filter values now compare structurally by default. Edits to any field commit, reordering an array counts as a change, and a new value equal to the committed one doesn’t commit. Pass isEqual to compare by id.
  • Custom filter drawer drafts now copy nested values. Arrays, plain objects, and Dates in the draft are copies, so compare items by a key rather than by reference.
  • Custom drawer cells now render in a group named by the filter’s label, with data-id set to the filter’s id. Query the control inside by role.
  • FilterRenderProps now requires isDraft, commit, close, and filter. Code that calls buttonRender or drawerRender directly must pass them.

Why Breaking?

Keeping the old defaults would keep the bugs: dropped edits and cleared filters that stayed active. These changes land while FilterBar is in beta, before they would require a major version.
Last modified on November 4, 2026