Filter controls
Use the Chip component to let users filter table rows in your app.
Use Chip components to let users filter which rows appear in a table by specific attributes, such as status or tier.
Before you begin
- Create an app or use an existing one.
- Install the latest version of the Stripe Apps CLI plugin .
- Review the Stripe Apps UI components .
Use Chip to filter table rows
Use Chip components to let users filter the rows shown in a DataTable. Each chip represents one filterable attribute, such as status or tier.
Wrap it in a Link and use it to trigger a Menu.
A filter chip has two states:
- Suggested (no value selected): The chip displays a plus symbol ( ) that opens a menu when clicked.
- Active (value selected): The chip displays the selected value with cancel symbol ( ) that clears the filter when clicked.
Render each state separately— wrapping an active chip in a Link causes onClose and the Link ’s press event to be sent simultaneously, which clears the filter and reopens the menu.
Build a filter bar with Chips
Arrange multiple FilterChip components above your DataTable with a Link labeled Clear filters at the end, shown only when at least one filter is active.
Filter the data before passing it as items to the DataTable to keep pagination and row counts accurate.
const MembersList = () => {
const [tierFilter, setTierFilter] = useState('');
const [statusFilter, setStatusFilter] = useState('');
const filteredMembers = membersData.filter((member) => {
const matchesTier = !tierFilter || member.tier === tierFilter;
const matchesStatus = !statusFilter || member.status === statusFilter;
return matchesTier && matchesStatus;
});
return (
<Box css={{stack: 'y', gap: 'medium'}}>
<Box css={{stack: 'x', gap: 'small', alignY: 'center'}}>
<FilterChip
label="Tier"
value={tierFilter}
options={[
{label: 'Bean Counter', value: 'Bean Counter'},
{label: 'Barista', value: 'Barista'},
{label: 'Roastmaster', value: 'Roastmaster'},
]}
onChange={setTierFilter}
/>
<FilterChip
label="Status"
value={statusFilter}
options={[
{label: 'Active', value: 'Active'},
{label: 'At risk', value: 'At risk'},
{label: 'Inactive', value: 'Inactive'},
]}
onChange={setStatusFilter}
/>
{(tierFilter || statusFilter) && (
<Link
onPress={() => {
setTierFilter('');
setStatusFilter('');
}}
>
<Inline css={{fontWeight: 'semibold'}}>Clear filters</Inline>
</Link>
)}
</Box>
<DataTable columns={columns} items={filteredMembers} />
</Box>
);
};
Best practices
- Match filter labels to column headers : Use labels that match the column names in the table they filter.
- Keep filter options short : Use concise, distinct labels that are easy to read.
- Provide a way to clear all filters : Display the Clear filters link at the end of the filter row, but only when at least one filter is active.
- Filter data before passing it to DataTable : Filter the source data before passing it to DataTable .
Limitations
- Chip has no built-in popover : Use the Link plus the Menu pattern described above.
- Active chips can’t be Menu triggers : Wrapping an active Chip (one with onClose ) inside a Link causes onClose and the Link ’s press event to be sent simultaneously —use two separate render paths instead.
- Menu only accepts MenuItem and MenuGroup children : For more complex filter interfaces (such as date ranges or multi-select with checkboxes), use dedicated field components, such as the DateRangePicker .
