A filtering framework for data-heavy products
Filters people can see, find and undo, in one row above your data. Install it with the shadcn CLI and make it yours.
Why it works this way"use client";
import { useState } from "react";
import { FilterBar } from "@/components/filter-bar/filter-bar";
import type { FilterDefinition, SortState } from "@/components/filter-bar/types";
import { useFilters } from "@/components/filter-bar/use-filters";
const definitions: FilterDefinition[] = [
{ id: "createdAt", label: "Created Date", type: "dateRange", tier: "quick" },
{
id: "status",
label: "Status",
type: "multiSelect",
tier: "quick",
options: [
{ value: "open", label: "Open" },
{ value: "closed", label: "Closed" },
],
},
{ id: "batchId", label: "Batch ID", type: "text", tier: "more" },
];
export function Documents() {
const filters = useFilters({ definitions });
const [sort, setSort] = useState<SortState>();
// filters.applied is what to filter by: send it to your API,
// or hand it to TanStack Table with the adapter.
return <FilterBar filters={filters} sort={sort} onSortChange={setSort} />;
}The details
Small interactions that keep filtering predictable. Each one is live; try it.
Rows never jump under your cursor
Selected options move to the top when the list opens, then stay put while you tick. Reopen it to see the new order.
- URL
- ?createdAt=last7d
- Today
- —
Humanised dates, in one click
“1 week ago”, not “2026-09-25 to 2026-10-01”. Presets read the way people talk, apply straight away, and stay relative in a shared link.
Pick a status
Add and remove with one control
The + turns into the × that removes the filter, and the chip eases to its new width, so neighbours slide instead of jump.
Important filters up front
A few quick filters stay visible. The rest are one search away in More Filters, and show as chips once they're used.
| B-10315 | Billing | Failed | Redaction | Bexa | API | 118 |
| B-10389 | Compliance | Initial | Escalation | Fenna | Fax | 64 |
| B-10241 | Intake | Committed | Standard Review | Dessa | Upload | 42 |
| B-10278 | Legal Review | In Review | Manual Indexing | Halvi | 7 | |
| B-10352 | Claims | Committed | Two-step Approval | Mirro | Upload | 3 |
Sort that doesn't scroll away
On a wide table the sorted column's header scrolls out of view. The sort chip keeps it in the toolbar, where you can flip or clear it.
Make it yours
Every size and colour is a CSS variable. Change them here and the whole site follows.
Installation
The shadcn CLI copies the source into your project, with the shadcn components, packages and CSS variables it needs. It needs a Radix-based shadcn style (init with --base radix); Base UI styles aren't supported yet.
npx shadcn@latest add https://your-deployment.example/r/filter-bar.jsonFiltering in the browser with TanStack Table? Add the adapter too:
npx shadcn@latest add https://your-deployment.example/r/filter-bar-tanstack.jsonUsage
Describe your filters, hand them to useFilters, render the bar.
"use client";
import { useState } from "react";
import { FilterBar } from "@/components/filter-bar/filter-bar";
import type { FilterDefinition, SortState } from "@/components/filter-bar/types";
import { useFilters } from "@/components/filter-bar/use-filters";
const definitions: FilterDefinition[] = [
{ id: "createdAt", label: "Created Date", type: "dateRange", tier: "quick" },
{
id: "status",
label: "Status",
type: "multiSelect",
tier: "quick",
options: [
{ value: "open", label: "Open" },
{ value: "closed", label: "Closed" },
],
},
{ id: "batchId", label: "Batch ID", type: "text", tier: "more" },
];
export function Documents() {
const filters = useFilters({ definitions });
const [sort, setSort] = useState<SortState>();
// filters.applied is what to filter by: send it to your API,
// or hand it to TanStack Table with the adapter.
return <FilterBar filters={filters} sort={sort} onSortChange={setSort} />;
}API
The main props. Filter definitions, the hook, URL sync and the adapter are in the docs.
| Prop | Type | Description |
|---|---|---|
| filters | UseFiltersResult | From useFilters({ definitions }). |
| sort, onSortChange | SortState | Shows the sort as a chip. |
| search | { value, onChange } | Optional search, applied as you type. |
| title, actions | ReactNode | Slots at either end of the bar. |
| resultCount | { shown, total } | Announces the result count to screen readers. |
| tooltips | boolean | Hover tooltips on chips. Default true. |