Data Table Filters
App-local data-table provider, toolbar, typed filter chips, search, and saved segments.
App-owned list filtering system used across org members, bookings, and other web tables. Callers own column definitions and queries; the App module owns URL state, segment tRPC wiring, and the stock toolbar composition.
import { DataTableProvider } from "~/data-table/DataTableProvider";
import { DataTableToolbar } from "~/data-table/components/data-table-toolbar";
import { DataTableWrapper } from "~/data-table/components/data-table-wrapper";
import { useSegments } from "~/data-table/hooks/useSegments";Ownership
App — not @coss/ui/shared. Lives under apps/web/modules/data-table because it depends on nuqs URL keys, Zustand table state, and trpc.viewer.filterSegments.* for saved segments. Filter type serializers live in packages/features/data-table; UI orchestration stays App-local.
Presentational Shared shells — FilterChipShell, FilterAddMenu, FilterBarActions, ColumnVisibilityMenu — are reused inside App wrappers. Do not relocate the store-bound system into Coss.
Architecture
DataTableProvider (tableIdentifier, useSegments, page size)
→ DataTableToolbar (SearchField, column visibility, FilterAddMenu, segments)
→ ActiveFilters (typed chips by ColumnFilterType)
→ DataTableWrapper (card table + pagination)
Use when
- A web list needs URL-synced filters, search, column visibility, and/or saved segments.
- You can pass a TanStack
tableinstance into the stock toolbar or compose App filter pieces yourself.
Prefer DataTableToolbar for the stock bar. Custom toolbars (bookings) still use App FilterAddMenu / ActiveFilters / segments — not Shared filter shells alone.
Canonical usage
Org members table is the stock composition — provider + default toolbar + wrapper:
return (
<DataTableProvider tableIdentifier={tableIdentifier} useSegments={useSegments} defaultPageSize={25}>
<UserListTableContent {...props} />
</DataTableProvider>
);<>
<DataTableToolbar table={table} />
<DataTableWrapper<UserTableUser>
testId="user-list-data-table"
tableState={tableState}
isPending={isPending}
loading={{
mode: "rows",
renderRow: (rowIndex) => <UserListSkeletonRow rowIndex={rowIndex} table={table} />,
}}
EmptyView={/* … */}
totalRowCount={data?.meta?.totalRowCount}
paginationMode="standard"
/>
</>DataTableToolbar defaults (showColumnVisibility, showSavedSegments, showSearch all true) compose SearchField → column visibility → FilterAddMenu → saved-segment combobox → ActiveFilters. Pass only table for the stock bar.
Custom toolbar (bookings)
Bookings skips DataTableToolbar and composes App pieces next to status tabs / view toggle — see BookingListContainer.tsx (FilterAddMenu, FilterSegmentSavedCombobox, conditional ActiveFilters). Same store; different chrome.
Filter types
ColumnFilterType | Chip | App import |
|---|---|---|
SINGLE_SELECT (ss) | SingleSelectFilter | ~/data-table/components/filters/single-select-filter |
MULTI_SELECT (ms) | MultiSelectFilter | ~/data-table/components/filters/multi-select-filter |
TEXT (t) | TextFilter | ~/data-table/components/filters/text-filter |
NUMBER (n) | NumberFilter | ~/data-table/components/filters/number-filter |
DATE_RANGE (dr) | DateRangeFilter | ~/data-table/components/filters/date-range-filter |
Also: SearchField, ColumnVisibilityButton, FilterAddMenu, ActiveFilters, FilterSegmentSavedCombobox under ~/data-table/components/....
Date range: App column filter ≠ Shared invoice widget
| App column filter | Shared widget | |
|---|---|---|
| Import | ~/data-table/components/filters/date-range-filter | @coss/ui/shared/daterange-filter |
| Docs | this page | Date Range Filter |
| Role | Column-aware chip wired to the data-table store | Invoice-oriented controlled popover (invoiceRange / presets) — no table store |
Do not substitute one for the other.
Why App-local
- URL state via
nuqs(activeFilters,q,segment,cols, pagination). - Saved segments via web tRPC.
- Table identity (
tableIdentifier) and column metadata are page-specific.
Reuse Shared shells for chrome; keep store/URL/segment wiring in apps/web/modules/data-table.
Call sites
| File | Notes |
|---|---|
apps/web/modules/users/components/UserTable/UserListTable.tsx | Stock DataTableToolbar |
apps/web/modules/bookings/components/BookingListContainer.tsx | Custom toolbar |
apps/web/modules/bookings/components/bookings-list.tsx | List body synced to table state |
Example URLs
Not exhaustive — a few places you can open locally (e.g. http://localhost:3000) to see this in product.
| Path | Notes |
|---|---|
/members | DataTableToolbar filters (org required) |
/bookings/upcoming | Bookings custom toolbar + filters |