Skip to content

Composite components

Higher-level components assembled from the primitives, for the patterns that recur across app surfaces. (Specialized, library-backed ones β€” charts, PDF viewer, kanban β€” are separate.)

Status
Acme Foodssubmitted$1,284
Brave Distributorsawaiting review$842
Cedar Supplyerror$96

Select rows, open the customer search, the delete confirm, pick a date range, or drop a file.

import DataTable, { type Column } from '@repo/ui/DataTable';
const columns: Column<Order>[] = [
{ key: 'customer', header: 'Customer', sortable: true },
{ key: 'total', header: 'Total', sortable: true, align: 'right', mono: true,
render: (r) => `$${r.total}` },
];
<DataTable columns={columns} rows={orders} getRowId={(o) => o.id}
selectable selectedIds={sel} onSelectionChange={setSel} emptyState={<EmptyState …/>} />

Data-driven table on the Table primitive: client-side sort, multi-select (header checkbox with indeterminate state), custom cell render, dense, onRowClick, empty state. Filtering stays app-level β€” filter the rows you pass.

<FileDropzone onFiles={fn} accept="application/pdf" multiple maxSizeMB={10} /> β€” drag-and-drop over a real <label>+<input type=file> (click, keyboard, SR all work). Reads dropped files via DataTransfer.items first (falls back to .files), so virtual-file drags β€” e.g. an attachment dragged straight out of Outlook on Windows β€” work too. Rejects oversize files.

<DateRangePicker value={{start,end}} onChange={fn} /> β€” two native date inputs bounded to each other (native calendar/keyboard/locale, no calendar dependency).

<ConfirmDialog open onOpenChange title message destructive confirmLabel onConfirm /> β€” focus-trapped confirm on Radix Dialog; destructive turns the confirm button red.

<SearchModal open onOpenChange query onQueryChange items getItemId renderItem onSelect /> β€” entity search. You own the (async) query + results; it owns the dialog + combobox/listbox keyboard selection (↑/↓/Enter).