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
Open the full demo: 2,000 rows, every filter type, filters in the URL

The details

Small interactions that keep filtering predictable. Each one is live; try it.

Select all
Contracts
Returns
Intake
Billing
Legal Review
Claims
Archive
Compliance
Underwriting
Correspondence
Vendor Invoices
Exceptions

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.

1 day ago
3 days ago
1 week ago
1 month ago
3 months ago
6 months ago
1 year ago
Custom date…
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-10315BillingFailedRedactionBexaAPI118
B-10389ComplianceInitialEscalationFennaFax64
B-10241IntakeCommittedStandard ReviewDessaUpload42
B-10278Legal ReviewIn ReviewManual IndexingHalviEmail7
B-10352ClaimsCommittedTwo-step ApprovalMirroUpload3

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.

Density
Radius
Unset
Accent

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.json

Filtering in the browser with TanStack Table? Add the adapter too:

npx shadcn@latest add https://your-deployment.example/r/filter-bar-tanstack.json

Usage

Describe your filters, hand them to useFilters, render the bar.

documents.tsx
"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.

PropTypeDescription
filtersUseFiltersResultFrom useFilters({ definitions }).
sort, onSortChangeSortStateShows the sort as a chip.
search{ value, onChange }Optional search, applied as you type.
title, actionsReactNodeSlots at either end of the bar.
resultCount{ shown, total }Announces the result count to screen readers.
tooltipsbooleanHover tooltips on chips. Default true.