Composite
Opinionated, finished patterns built on top of the primitives.
These 16 components are higher-level than primitives — they're the patterns we'd otherwise hand-roll on every project. Each one bundles a few primitives + tokens + a sensible default behavior so you can drop it in and move on.
Banner
System-wide announcement bar. Supports dismissal, info / warning / error tones, and a CTA slot.
import { Banner } from '@otfdashkit/ui'
<Banner tone="info" dismissible>
Heads up — we're moving to v2 next week. <a href="/changelog">Read more</a>
</Banner>Breadcrumb
Slash-separated path with the last segment shown as plain text.
<Breadcrumb items={[
{ label: 'Workspace', href: '/' },
{ label: 'Settings', href: '/settings' },
{ label: 'Notifications' },
]} />CommandItem
A single row inside a command palette — icon + label + optional shortcut hint.
<CommandItem icon={<Settings />} shortcut="⌘,">
Open settings
</CommandItem>DataGrid
Read-only data renderer for sparse / heterogeneous rows. For full table behavior (sort,
filter, bulk-select), use DataTable.
<DataGrid
columns={[
{ key: 'name', label: 'Name' },
{ key: 'role', label: 'Role' },
{ key: 'lastSeen', label: 'Last seen', render: (v) => <RelativeTime value={v} /> },
]}
rows={users}
/>DataTable
The tablestakes table — headers, sortable columns, optional row selection, optional bulk-action
bar wired through to <BulkActions>. Columns are typed; rows are inferred.
<DataTable
data={issues}
columns={[
{ accessorKey: 'title', header: 'Title' },
{ accessorKey: 'status', header: 'Status' },
{ accessorKey: 'assignee', header: 'Assignee' },
]}
enableRowSelection
onRowSelectionChange={setSelected}
/>EmptyState
Centered placeholder with an icon, title, description, and a CTA. Use it everywhere a list would otherwise be empty.
<EmptyState
icon={<Inbox />}
title="Inbox zero"
description="You're all caught up. New notifications appear here."
action={<Button>Configure alerts</Button>}
/>Hotkeys
Render a keyboard chord with platform-correct symbols (⌘ on macOS, Ctrl elsewhere).
<Hotkeys keys={['mod', 'k']} /> {/* renders ⌘K on Mac, Ctrl+K on Windows */}IconBadge
A square or pill background behind a Lucide icon — mostly used inside Persona, Stat, list rows.
<IconBadge icon={<Bell />} tone="primary" size="md" />LoadingOverlay
Full-element overlay with spinner. Useful for "saving" / "syncing" states without unmounting the underlying form.
<div className="relative">
<Form ... />
{isSubmitting && <LoadingOverlay />}
</div>Persona
Avatar + name + role on one row. Optional subtitle, status dot, and trailing actions.
<Persona
name="Sarah Chen"
subtitle="Product Lead, Northbeam"
imageUrl="/avatars/sarah.jpg"
size="md"
ring
/>PropertyList
Two-column key/value list — used for "details" panes (issue, project, settings).
<PropertyList
items={[
{ label: 'Status', value: <Badge>Active</Badge> },
{ label: 'Created', value: 'Sep 24, 2024' },
{ label: 'Owner', value: <Persona name="Alex Rivera" size="sm" /> },
]}
/>Stat
Big-number + label + optional delta with trend arrow. The KPI tile.
<Stat
label="Active users"
value="12,406"
delta="+12% vs last week"
trend="up"
/>Stepper
Horizontal or vertical multi-step progress indicator with direction prop.
<Stepper
steps={['Account', 'Workspace', 'Invite team', 'Done']}
current={1}
direction="horizontal"
/>StructuredList
Vertically-stacked rows of (icon · title · subtitle · trailing). Used for settings, profile,
notification feeds — anywhere you'd reach for a <ul> and end up styling it.
<StructuredList items={[
{ icon: <Bell />, title: 'Email notifications', trailing: <Switch /> },
{ icon: <Lock />, title: 'Two-factor auth', trailing: <Badge>Off</Badge> },
]} />Timeline
Vertical activity feed with timestamps and optional avatars per entry.
<Timeline items={[
{ time: '2h ago', title: 'Sarah commented on #42', icon: <MessageSquare /> },
{ time: '5h ago', title: 'Alex resolved #41', icon: <Check /> },
]} />Toaster
Mounted once at the root; pairs with the imperative toast() function.
import { Toaster, toast } from '@otfdashkit/ui'
<Toaster />
toast.success('Saved')
toast.error('Network error', { description: 'Check your connection and try again.' })