Filter
The faceted filter control above a table or list — a trigger carrying the applied count, and a popover of checklists grouped by facet with a per-option tally, multi-select inside and across facets, and a clear action. Exported as FilterMenu, with the panel, trigger, group and row available separately so the same checklist can be mounted somewhere else. The applied-count badge takes --selected rather than the count badge's --info on purpose: a tally that says “this control is switched on” is selection, not information.
import {
FilterMenu, FilterMenuPanel, FilterMenuTrigger, countFilterSelection,
} from "@pestoui/core"Default
Uncontrolled and live: every checkbox lands immediately, and the badge on the trigger counts the values across every facet. The footer mounts with the first applied filter and carries Clear all — which hands focus to the search box or the first checkbox on its way out, because the button unmounts the instant it succeeds. Each facet is a role="group" labelled by its heading, which is what lets two facets that happen to share an option label still be told apart — the rows themselves carry no visible qualifier.
Searchable
searchable adds a box above the list that narrows options by label and drops any facet left with nothing. When the query matches nothing at all the body falls back to an EmptyState — and emptyTitle / emptyDescription override the copy for both that case and the one where there is genuinely nothing to filter on.
Deferred with Apply
Passing onApply renders the Apply button and switches the panel to a draft: the badge then counts the last committed set rather than what is currently checked. Three consequences worth knowing — closing without applying keeps the draft, so reopening resumes where you left off, but the badge does not move because nothing was committed; Clear all edits the draft like any other control, so it still needs an Apply to take effect; and onValueChange keeps firing on every toggle, which is the draft, not the commit.
Applied: status: active
Trigger
label renames the button, variant and size take it down to a toolbar control, and disabled refuses the popover outright. The chevron is one glyph that turns a half turn, so the resting shape is identical in both states and only the change between them is animated.
The panel on its own
FilterMenuPanel is the body and footer with no popover chrome, so the same checklist can be laid flat in a sidebar or mounted in a Sheet on a narrow viewport. surface="flush" makes it paint its own card; the default assumes PopoverContent has already painted one. It is fully controlled by design — there is deliberately no prop that swaps its own container, because two mount points sharing one draft is a state-duplication trap. countFilterSelection is exported so a consumer tallies the applied set exactly the way the badge does.
0 applied
Trigger and panel mounted by hand
FilterMenu is the trigger and the panel wired into a Popover for you. Both halves are exported, so a surface that is not a Popover — a Sheet on a narrow viewport, a panel docked into a page — can mount FilterMenuPanel itself and still use FilterMenuTrigger for the button, keeping the applied-count badge and the chevron identical to every other filter in the product. count is passed in rather than derived, because a hand-mounted panel owns the selection; pass countFilterSelection(selection) and the tally matches what FilterMenu would have shown. The badge takes the selected accent, not the badge's own count colours — an applied tally is selection semantics, not information.
Props
Pesto UI · 117 documented components