Skip to content

DataTable ​

A data table with sorting, row selection, pagination, loading and empty states, built on an accessible native <table>.

Usage ​

Sorting, selection and pagination ​

Selection: none

vue
<script setup>
import { ref } from 'vue'

const columns = [
  { key: 'name', label: 'Customer', sortable: true },
  { key: 'city', label: 'City', sortable: true },
  { key: 'orders', label: 'Orders', sortable: true, align: 'end' },
  { key: 'status', label: 'Status' }
]
const rows = ref([/* { id, name, city, orders, status } */])
const sort = ref(null)
const page = ref(1)
const selected = ref([])
</script>

<template>
  <SuDataTable
    v-model:sort="sort"
    v-model:page="page"
    v-model:selected="selected"
    :columns="columns"
    :rows="rows"
    caption="Customers"
    selectable
    :page-size="5"
  >
    <template #cell-status="{ value }">
      <SuTag :label="value" size="sm" />
    </template>
  </SuDataTable>
</template>

Format, alignment and actions column ​

format changes the display without changing the sorted value; hideLabel visually hides a header (actions column) that is still read.

js
const columns = [
  { key: 'revenue', label: 'Revenue', sortable: true, align: 'end',
    format: (value) => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value) },
  { key: 'since', label: 'Customer since', sortable: true,
    format: (value) => value.toLocaleDateString('en-US') },
  { key: 'actions', label: 'Actions', hideLabel: true, align: 'end' }
]

Server-side sorting and pagination ​

With manual, rows are shown as received: the table emits update:sort and update:page, the server returns the sorted page and total sets the page count.

vue
<SuDataTable
  v-model:sort="sort"
  v-model:page="page"
  :columns="columns"
  :rows="pageRows"
  :total="total"
  :page-size="20"
  :loading="loading"
  manual
  caption="Orders"
  @update:sort="fetchPage"
  @update:page="fetchPage"
/>

Loading and empty state ​

vue
<SuDataTable :columns="columns" :rows="rows" :loading="loading" />
<SuDataTable :columns="columns" :rows="[]" empty-text="No customers match the filters." />

API ​

Props ​

PropTypeDefaultDescription
columnsDataTableColumn[][]Columns
rowsRecord<string, any>[][]Rows
rowKeystring | (row) => string | number'id'Row identifier
captionstring-Table title, region name (or caption slot)
captionHiddenbooleanfalseTitle read but visually hidden
selectablebooleanfalseSelection checkboxes
rowLabel(row) => stringfirst columnRow name for its checkbox
pageSizenumber-Rows per page (pagination)
manualbooleanfalseSorting and pagination done by the server
totalnumber-Total rows in manual mode
loadingbooleanfalseLoading: skeletons and aria-busy
emptyTextstring'No data' (locale)Empty state text (or empty slot)
stripedbooleanfalseAlternating rows
size'sm' | 'md' | 'lg''md'Density
stickyHeaderbooleanfalseSticky header (with maxHeight)
maxHeightstring-Maximum height (scrolling)
localestringcomponent localeAlphabetical sort language

DataTableColumn ​

PropertyTypeDescription
keystringRow property
labelstringHeader
sortablebooleanSortable column
align'start' | 'center' | 'end'Alignment (end: tabular figures)
widthstringWidth
format(value, row) => stringDisplayed text
sortFn(a, b) => numberCustom comparison
hideLabelbooleanVisually hidden header

v-model ​

ModelTypeDescription
v-model:sort{ key, direction: 'asc' | 'desc' } | nullCurrent sort
v-model:pagenumberCurrent page
v-model:selected(string | number)[]Checked row ids

Slots ​

SlotPropsDescription
cell-{key}{ row, value, index }Cell content
header-{key}{ column }Header content
caption-Rich title
empty-Empty state (e.g. SuEmptyState)

Accessibility ​

  • Native <table>: <caption>, column headers (scope="col") and row headers (first column, scope="row").
  • Sorting: button in the header, aria-sort on the sorted column, announcement "Sorted by City, ascending". Language-aware alphabetical order (accents, numbers inside text), empty cells always last.
  • Selection: each checkbox is named after its row ("Select Émile A."); the "Select all" checkbox carries the mixed state and acts on the displayed rows.
  • The scroll area is focusable (keyboard scrolling) and named by the title.
  • Loading: aria-busy="true" and a "Loading…" text read inside the table.

Publié sous licence MIT.