Filter toolbar
FreeA search field and filter menus with result counts, for narrowing a list.
pnpm dlx shadcn@latest add https://caliperui.site/r/filter-toolbar.json4 of 4 projects
- Atlas platform
- Harbour redesign
- Release notes
- Wayfinder
When to use #
Use it for
- Directories and tables with a few categorical filters.
- You want an accessible count of matching results.
Skip it when
- The list is short enough to scan.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/filter-toolbar.json
Adds its 8 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above. It is built from FilterToolbar and countOptions.
import { useMemo, useState } from "react" import { FilterToolbar, type FilterValue } from "@/components/ui/filter-toolbar" const projects = [ { id: "atlas", name: "Atlas platform", owner: "Mara Chen", status: "Active", budget: 48000 }, { id: "harbour", name: "Harbour redesign", owner: "Noor Haddad", status: "In review", budget: 21000 }, { id: "notes", name: "Release notes", owner: "Ivo Petrov", status: "Active", budget: 6500 }, { id: "wayfinder", name: "Wayfinder", owner: "Sana Okafor", status: "Paused", budget: 33000 }, ]; export function ProjectFilters() { const [query, setQuery] = useState(""); const [filters, setFilters] = useState<FilterValue>({}); const shown = useMemo( () => projects.filter((project) => project.name.toLowerCase().includes(query.toLowerCase()) && (!filters.status?.length || filters.status.includes(project.status))), [query, filters], ); return ( <div className="grid w-full max-w-2xl gap-3"> <FilterToolbar label="Projects" query={query} onQueryChange={setQuery} placeholder="Search projects" filters={[{ id: "status", label: "Status", options: ["Active", "In review", "Paused"].map((value) => ({ value, label: value })) }]} value={filters} onValueChange={setFilters} resultCount={shown.length} totalCount={projects.length} noun="projects" /> <ul className="text-sm text-muted-foreground">{shown.map((project) => <li key={project.id}>{project.name}</li>)}</ul> </div> ); }
API reference #
FilterToolbar
component
countOptions
component
No props of its own; it accepts the native props of its root element.
Accessibility #
It exposes search and status semantics, with aria-label, aria-hidden and aria-live kept in step with state. Changes that matter are announced through a live region.
Keyboard and focus handling comes from Radix dropdown-menu: arrow keys move through items, Enter and Space select, and Escape closes and returns focus.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.