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 Foods | submitted | $1,284 | |
| Brave Distributors | awaiting review | $842 | |
| Cedar Supply | error | $96 |
Select rows, open the customer search, the delete confirm, pick a date range, or drop a file.
DataTable
Section titled βDataTableβ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
Section titled βFileDropzoneβ<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
Section titled βDateRangePickerβ<DateRangePicker value={{start,end}} onChange={fn} /> β two native date inputs bounded to each
other (native calendar/keyboard/locale, no calendar dependency).
ConfirmDialog
Section titled βConfirmDialogβ<ConfirmDialog open onOpenChange title message destructive confirmLabel onConfirm /> β focus-trapped
confirm on Radix Dialog; destructive turns the confirm button red.
SearchModal
Section titled βSearchModalβ<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).