Skip to content

Data

Two components for displaying tabular / chronological data.

Table

Data table with sorting, selection, sticky header, loading skeleton, and empty state.

ts
interface TableColumn {
  key: string
  label: string
  sortable?: boolean
  width?: string           // CSS length
  align?: 'left' | 'center' | 'right'
}

interface Props {
  columns: TableColumn[]
  rows: Record<string, any>[]
  loading?: boolean
  selectable?: boolean | 'single' | 'multi'   // 'single' | 'multi' for checkbox column
  striped?: boolean
  size?: 'compact' | 'comfortable'   // 'comfortable'
  stickyHeader?: boolean             // true
  rowKey?: string | ((row, index) => string | number)   // 'id'
  skeletonRows?: number              // 5 — how many loading rows to render
}

Slots

Per-cell rendering via #cell-{column.key} (or #cell-{column.key}-{row.id} for one-off overrides). #empty for the empty state body, #header-actions for a bulk-action bar that appears when rows are selected.

vue
<Table
  :columns="[
    { key: 'name',    label: 'Name',    sortable: true },
    { key: 'size',    label: 'Size',    align: 'right' },
    { key: 'updated', label: 'Updated', sortable: true, width: '140px' },
    { key: 'actions', label: '',        width: '60px' },
  ]"
  :rows="files"
  :loading="loading"
  selectable="multi"
  striped
  v-model:selected="selectedRows"
>
  <template #cell-size="{ row }">
    {{ bytes(row.size) }}
  </template>

  <template #cell-actions="{ row }">
    <Dropdown :items="rowActions(row)">
      <Button icon="i-lucide-more-horizontal" variant="ghost" size="xs" />
    </Dropdown>
  </template>

  <template #empty>
    <Empty icon="i-lucide-folder-open" title="No files yet" />
  </template>
</Table>

Timeline

Vertical or horizontal sequence of events with status indicators.

ts
interface TimelineItem {
  id: string
  title: string
  description?: string
  icon?: string
  color?: string
  timestamp?: string | Date
  status?: 'completed' | 'active' | 'pending'
}

interface Props {
  items?: TimelineItem[]
  direction?: 'vertical' | 'horizontal'    // 'vertical'
  size?: 'compact' | 'comfortable'         // 'comfortable'
}
vue
<Timeline :items="[
  { id: '1', title: 'Created folder',     status: 'completed', timestamp: '2026-04-12T09:00' },
  { id: '2', title: 'Shared with team',   status: 'completed', timestamp: '2026-04-12T10:32' },
  { id: '3', title: 'Awaiting approval',  status: 'active',    icon: 'i-lucide-clock' },
  { id: '4', title: 'Publish to org',     status: 'pending' },
]" />

Released under the MIT License.