Components

Datatable

Display tabular data with sorting, a column manager, loading, and empty states.

UiDatatable renders rows from an items array against an explicit columns definition. Each column's key is the data field, the sort key, and the cell-<key> slot name; label is the header text.

Add a datatable

<script setup lang="ts">
import type { IDatatableColumn } from '@colorffy/ui'

const columns: IDatatableColumn[] = [
  { key: 'name', label: 'Name' },
  { key: 'email', label: 'Email' },
  { key: 'total', label: 'Total', align: 'end' },
]

const items = [
  { name: 'John Doe', email: 'john@example.com', total: 1280 },
  { name: 'Jane Smith', email: 'jane@example.com', total: 940 },
]
</script>

<template>
  <UiDatatable
    :columns="columns"
    :items="items"
    default-sort-key="name"
    table-class="table-bordered"
  />
</template>

Customize a cell

The cell slot name is cell-<key>:

<template>
  <UiDatatable :columns="columns" :items="items">
    <template #cell-total="{ item }">
      <strong>${{ item.total }}</strong>
    </template>
  </UiDatatable>
</template>

Hide, align, and manage columns

Mark a column hidden to start it collapsed, then enable column-manager so users can toggle it:

<script setup lang="ts">
import type { IDatatableColumn } from '@colorffy/ui'

const columns: IDatatableColumn[] = [
  { key: 'id', label: 'ID', hidden: true },
  { key: 'name', label: 'Name' },
  { key: 'actions', label: 'Actions', sortable: false, align: 'end' },
]
</script>

<template>
  <UiDatatable :columns="columns" :items="items" column-manager>
    <template #cell-actions="{ item }">
      <UiButton variant="outline" size="sm" :text="`Edit ${item.name}`" />
    </template>
  </UiDatatable>
</template>

Select rows

Set selectable and bind v-model:selected to an array of row identities. A row's identity is the same value used for its :keyrowKey if set, else the row's id, else its array index (see Props). The header checkbox selects/clears every currently sorted row and shows an indeterminate state when only some are selected; selection stays attached to the row across sorting as long as rows have a stable id (or rowKey).

<script setup lang="ts">
import { ref } from 'vue'

const selected = ref<(string | number)[]>([])
</script>

<template>
  <UiDatatable
    :columns="columns"
    :items="items"
    selectable
    v-model:selected="selected"
  />
</template>

Stick the header while scrolling

Set stickyHeader to keep the header visible while the body scrolls. It automatically wraps the table in a bounded, vertically-scrollable container (.table-responsive-sticky); override its height with the --_table-sticky-max-height CSS variable (default 32rem):

<template>
  <UiDatatable
    :columns="columns"
    :items="items"
    sticky-header
    style="--_table-sticky-max-height: 20rem"
  />
</template>

Show loading and empty states

<template>
  <!-- Skeleton rows while data loads -->
  <UiDatatable :columns="columns" :items="[]" :is-loading="true" :skeleton-rows="6" />

  <!-- Built-in empty state -->
  <UiDatatable
    :columns="columns"
    :items="[]"
    empty-state-title="No customers yet"
    empty-state-subtitle="New customers will appear here."
  />
</template>

Props

PropTypeDefaultDescription
columnsIDatatableColumn[]Column definitions (required).
itemsRecord<string, any>[]Row data (required).
rowKeystringid → indexStable key field for rows; also the row-selection identity.
selectablebooleanfalseShow a leading checkbox column. Pair with v-model:selected.
selected(string | number)[][]Selected row identities. Bind with v-model:selected.
stickyHeaderbooleanfalseStick the header while the body scrolls (wraps in .table-responsive-sticky).
sortablebooleantrueEnable sorting (per-column opt-out via column.sortable).
defaultSortKeystring''Column key to sort by initially.
defaultSortOrder'asc' | 'desc''asc'Initial sort direction.
columnManagerbooleanfalseShow the show / hide column menu.
tableClass'table-bordered' | 'table-striped' | 'table-borderless' | string''Table style.
isLoadingbooleanfalseShow skeleton rows.
skeletonRows / skeletonColsnumber10 / 5Skeleton dimensions.
captionstringAccessible <caption>.
emptyStateTitlestring'No data available'Empty-state title.
emptyStateSubtitlestringEmpty-state subtitle.

Column (IDatatableColumn): key, label, sortable?, hidden?, align? (start / center / end), thClass?, tdClass?. Slots: cell-<key> per column.

Copyright © 2026