@tc96/filter-builder
v0.1.0
Published
Domain-neutral filter builder for React applications using COSS and Base UI.
Maintainers
Readme
Filter Builder
A domain-neutral, accessible filter builder for React applications built with COSS, Base UI, and Tailwind CSS.
The package provides the interaction and presentation layer for filtering SaaS list, table, and board views. Your application remains responsible for the attribute catalog, operators, option sources, permissions, remote state, querying, and persistence.
Highlights
- generic typed attributes with consumer-defined operators and options
- controlled value contract with an internal draft while the popover is open
- cascading resets: changing an attribute drops incompatible operators/values
- apply gate that stays disabled until every condition is complete
- clear action that resets back to a single empty condition
- accessible popover, form, and grouped selects on Base UI
- responsive condition rows for desktop and mobile
- COSS visual primitives implemented on Base UI
Requirements
- React 19
- Base UI 1.x
- Tailwind CSS 4
The package is COSS-first. COSS is distributed as source through its component registry, so this repository owns the small set of COSS primitives required by the view. A future Radix implementation will be a separate adapter and will not change the domain-neutral contract.
Installation
Install the published package with your package manager:
pnpm add @tc96/filter-builder
# or: bun add @tc96/filter-builder
# or: npm install @tc96/filter-builderThe consumer must already provide the peer dependencies:
pnpm add react react-dom @base-ui/react tailwindcssCOSS source location
@tc96/filter-builder is installed from npm and imported from node_modules.
It does not need a components.json. Add that file only when the consuming app
uses the COSS/shadcn CLI. Map the ui alias to components/patterns so COSS
primitives are owned by the app in that directory:
{
"$schema": "https://ui.shadcn.com/schema.json",
"tsx": true,
"aliases": {
"ui": "@/components/patterns"
}
}Then run pnpm dlx shadcn@latest add @coss/ui (or the Bun/npm equivalent).
Tailwind must scan the installed package, and your theme must expose the
standard COSS semantic tokens (--color-secondary, --color-popover,
--color-muted-foreground, --color-border, --color-input, --color-ring,
--radius, --radius-control, and related pairs):
@import "tailwindcss";
@source "../node_modules/@gblsmlo/filter-builder/dist";Quick start
import {
FilterBuilder,
type FilterBuilderAttribute,
type FilterCondition,
} from "@gblsmlo/filter-builder";
import { useState } from "react";
const attributes: FilterBuilderAttribute[] = [
{
id: "status",
label: "Status",
valueType: "select",
operators: [
{ label: "is", value: "is" },
{ label: "is not", value: "is-not" },
],
options: [
{ label: "In progress", value: "in-progress" },
{ label: "Done", value: "done" },
],
},
];
export function ListFilters() {
const [conditions, setConditions] = useState<FilterCondition[]>([]);
return (
<FilterBuilder
attributes={attributes}
onValueChange={setConditions}
value={conditions}
/>
);
}onValueChange receives the complete condition set only when the user applies
the filter, or an empty array when they clear it. The component is fully
controlled: pass the applied value back in and it rebuilds its draft on open.
Use @gblsmlo/filter-builder/core when a non-visual layer only needs the public
types and condition helpers (isCompleteFilterCondition,
createFilterConditionDraft).
API
FilterBuilder
| Prop | Type | Description |
| --- | --- | --- |
| attributes | readonly FilterBuilderAttribute[] | Attribute catalog with per-attribute operators and options |
| value | readonly FilterCondition[] | Applied conditions owned by the consumer |
| onValueChange | (conditions: FilterCondition[]) => void | Called on apply (complete set) or clear (empty) |
| triggerLabel | string | Optional trigger button label (defaults to Filtrar) |
Types
interface FilterBuilderOption {
label: string;
value: string;
}
interface FilterBuilderAttribute {
id: string;
label: string;
operators: readonly FilterBuilderOption[];
options: readonly FilterBuilderOption[];
valueType: "select";
}
interface FilterCondition {
attributeId: string;
operator: string;
value: string;
}Ownership boundary
| Package owns | Consumer owns | | --- | --- | | popover, form, and grouped condition layout | attribute catalog, operators, and options | | draft editing, cascading resets, and apply gating | meaning of each attribute and operator | | accessible controls and responsive rows | query construction and server filtering | | controlled apply/clear callback | persisting, sharing, and restoring filters |
The package intentionally supports select value conditions joined by an
implicit AND. Additional value types and boolean grouping remain
consumer/server-owned until a durable contract is defined.
Development
Use Bun 1.3.14 and Node 24.18.0.
bun install
bun run storybook
bun run lint:ci
bun run typecheck
bun test
bun run buildSee CONTRIBUTING.md for the contribution workflow and ADR-001 for the adapter decision.
License
MIT © Gabriel Melo.
