Skip to content

Composables

useDataTable

Everything ITable does, with none of the markup: sorting, pagination, selection and expansion. Use it when the rows are laid out as cards, a list or a board rather than a <table>.

INV-1042Acme Industries€4,820
INV-1041Bolt Logistics€1,290
INV-1040Cirrus Systems€9,600

Options

rows and columns are getters rather than values, so the composable stays reactive to whatever holds them — a ref, a prop, or a store.

OptionTypeWhat it does
rows() => T[]The data. Required
columns() => TableColumn<T>[]Column definitions. Required
total() => number | undefinedRow count on the server. Its presence switches modes
rowKey() => stringField identifying a row, for selection and expansion
sortModelRef<TableSort | null>Controlled sort; omit for internal state
pageModelRef<number>Controlled page
perPageModelRef<number>Controlled page size
selectionModelRef<(string | number)[]>Controlled selection
expandedModelRef<(string | number)[]>Controlled expansion
isRowSelectable(row: T) => booleanRows the header checkbox skips

Every model is optional. Bind one and you own that piece of state; leave it out and the composable keeps its own — so the page, sort and selection can live in a store, in the URL, or nowhere at all.

Client and server mode

Passing total is the switch. Without it the composable sorts and slices the rows you gave it. With it, the rows you passed are already the page the server returned, so sorting and paging only change state — watch sort and page, and fetch.

ts
const table = useDataTable({
  rows: () => data.value.rows,
  columns: () => columns,
  total: () => data.value.total, // server mode
})

watch([table.sort, table.page, table.perPage], fetchPage)

isServerMode is exposed so your own UI can branch on it.

What you get back

ReturnedType
sort / page / perPagerefsThe state, controlled or internal
selection / expandedrefsArrays of row keys
isServerModecomputedtrue when total was supplied
visibleColumnscomputedColumns left after hidden is applied
pageRowscomputedThe rows to render, sorted and sliced in client mode
totalcomputedServer total, or the row count
pageCountcomputedPages at the current size
keyOf(row)fnThe row's key, via rowKey
toggleSort(column)fnCycles ascending → descending → none
sortOrderFor(column)fn'asc', 'desc' or undefined
headerSelectioncomputedtrue, false or 'indeterminate'
isSelected / toggleRowfnPer-row selection
toggleAllfnSelects the selectable rows on the page
isExpanded / toggleExpandedfnPer-row expansion
getRowValue(row, path)fnDot-notation accessor, exported separately too

Columns

ts
interface TableColumn<T = any> {
  key: string
  label?: string
  sortable?: boolean
  sortKey?: string
  align?: 'start' | 'center' | 'end'
  numeric?: boolean
  width?: string
  class?: string | ((row: T) => string)
  hidden?: boolean | ((row: T) => boolean)
}

sortKey covers the case where the field you display and the field the server sorts by have different names — customer.name against customer_name.

As with ITable, fetching and caching stay in your data layer.