Skip to main content

Overview

A composable data table that provides sort controls, filtering, pagination, row selection, expansion, and grouping. Uses an adapter pattern for data pipeline strategy (client-side, server-side, or virtual scrolling).

Basic Usage

import { createDataTable } from '@vuetify/v0'

interface User {
  id: number
  name: string
  email: string
  department: string
}

const users: User[] = [
  { id: 1, name: 'Alice', email: '[email protected]', department: 'Engineering' },
  { id: 2, name: 'Bob', email: '[email protected]', department: 'Design' },
  { id: 3, name: 'Carol', email: '[email protected]', department: 'Engineering' },
]

const table = createDataTable({
  items: users,
  columns: [
    { key: 'name', title: 'Name', sortable: true, filterable: true },
    { key: 'email', title: 'Email', sortable: true, filterable: true },
    { key: 'department', title: 'Dept', sortable: true },
  ],
})

// Search
table.search('alice')

// Sort
table.sort.toggle('name')

// Paginate
table.pagination.next()

// Select rows
table.selection.toggle(1)

Function Signature

function createDataTable<T extends Record<string, unknown>>(
  options: DataTableOptions<T>
): DataTableContext<T>

Parameters

options
DataTableOptions<T>
required
Configuration options for the data table
items
MaybeRefOrGetter<T[]>
required
Source items (array or ref)
columns
DataTableColumn<T>[]
required
Column definitions
{
  key: 'name',
  title: 'Name',
  sortable: true,
  filterable: true,
  sort: (a, b) => customComparator(a, b),
  filter: (value, query) => customFilter(value, query),
}
itemValue
string
default:"'id'"
Property used as row identifier (must be string or number)
sortMultiple
boolean
default:"false"
Enable multi-column sort
mandate
boolean
default:"false"
Prevent clearing sort (asc ↔ desc cycle only)
firstSortOrder
'asc' | 'desc'
default:"'asc'"
Direction for first sort click
selectStrategy
'single' | 'page' | 'all'
default:"'page'"
Row selection strategy
itemSelectable
keyof T
Property that controls per-row selectability
groupBy
keyof T
Column key to group rows by
enroll
boolean
default:"false"
Auto-open all groups on creation
expandMultiple
boolean
default:"true"
Allow multiple rows expanded simultaneously
pagination
PaginationOptions
Pagination configuration
filter
FilterOptions
Filter configuration
locale
string
Locale for sorting (defaults to browser default)
adapter
DataTableAdapterInterface
Data pipeline adapter (ClientAdapter, ServerAdapter, VirtualAdapter)

Returns

DataTableContext<T>
object
Data table context with pipeline stages and controls
items
Ref<readonly T[]>
Final paginated items for rendering
allItems
Ref<readonly T[]>
Raw unprocessed items
filteredItems
Ref<readonly T[]>
Items after filtering
sortedItems
Ref<readonly T[]>
Items after filtering and sorting
Set the search query
query
ShallowRef<string>
Current search query (readonly)
sort
DataTableSort
Sort controls and state
pagination
PaginationContext
Pagination controls
selection
DataTableSelection
Row selection controls
expansion
DataTableExpansion
Row expansion controls
grouping
DataTableGrouping<T>
Row grouping controls
total
Ref<number>
Total row count for aria-rowcount
loading
Ref<boolean>
Loading state (managed by adapter)
error
Ref<Error | null>
Error state (managed by adapter)

Sorting

Basic Sort Cycle

Default cycle: none → asc → desc → none
const table = createDataTable({ items, columns })

table.sort.toggle('name') // asc
table.sort.toggle('name') // desc
table.sort.toggle('name') // none

Reverse First Sort Order

Start with descending:
const table = createDataTable({
  items,
  columns,
  firstSortOrder: 'desc',
})

table.sort.toggle('name') // desc first

Mandate Sort

Prevent clearing sort:
const table = createDataTable({
  items,
  columns,
  mandate: true,
})

table.sort.toggle('name') // asc
table.sort.toggle('name') // desc
table.sort.toggle('name') // asc (no clear)

Multi-Column Sort

const table = createDataTable({
  items,
  columns,
  sortMultiple: true,
})

table.sort.toggle('department') // Sort by department first
table.sort.toggle('name')       // Then by name

console.log(table.sort.columns.value)
// [{ key: 'department', direction: 'asc' }, { key: 'name', direction: 'asc' }]

console.log(table.sort.priority('department')) // 0
console.log(table.sort.priority('name'))       // 1

Custom Sort Comparator

const columns: DataTableColumn<User>[] = [
  {
    key: 'salary',
    title: 'Salary',
    sortable: true,
    sort: (a, b) => Number(a) - Number(b),
  },
]

const table = createDataTable({ items, columns })
table.sort.toggle('salary')

Sort Methods

// Get sort direction for a column
table.sort.direction('name') // 'asc' | 'desc' | 'none'

// Get sort priority (0-based index)
table.sort.priority('name') // 0 | -1 if not sorted

// Reset all sort state
table.sort.reset()

Filtering

Search Query

const table = createDataTable({
  items,
  columns: [
    { key: 'name', filterable: true },
    { key: 'email', filterable: true },
  ],
})

table.search('alice')
console.log(table.filteredItems.value) // Items matching 'alice'

table.search('') // Clear filter

Custom Column Filter

const columns: DataTableColumn<User>[] = [
  {
    key: 'name',
    filterable: true,
    filter: (value, query) => {
      return String(value).toLowerCase().startsWith(query)
    },
  },
]

Pagination

const table = createDataTable({
  items,
  columns,
  pagination: {
    itemsPerPage: 10,
  },
})

// Navigate pages
table.pagination.next()
table.pagination.prev()
table.pagination.goto(3)

// Check state
console.log(table.pagination.page.value)         // Current page
console.log(table.pagination.itemsPerPage.value) // Items per page
console.log(table.pagination.total.value)        // Total pages

Selection

Basic Selection

const table = createDataTable({ items, columns })

// Select/unselect rows
table.selection.select(1)
table.selection.unselect(1)
table.selection.toggle(1)

// Check state
table.selection.isSelected(1) // boolean
console.log(table.selection.selectedIds) // Set<ID>

Selection Strategies

Single: Only one row at a time
const table = createDataTable({
  items,
  columns,
  selectStrategy: 'single',
})

table.selection.select(1)
table.selection.select(2) // Clears row 1
Page: Operates on visible items only
const table = createDataTable({
  items,
  columns,
  selectStrategy: 'page',
  pagination: { itemsPerPage: 10 },
})

table.selection.selectAll() // Selects 10 visible items
All: Operates on all filtered items
const table = createDataTable({
  items,
  columns,
  selectStrategy: 'all',
})

table.selection.selectAll() // Selects all items

Selectable Rows

Control per-row selectability:
interface User {
  id: number
  name: string
  active: boolean
}

const table = createDataTable({
  items,
  columns,
  itemSelectable: 'active',
})

// Inactive users cannot be selected
table.selection.select(inactiveUserId) // No-op
table.selection.isSelectable(inactiveUserId) // false

Bulk Selection

// Select all in scope
table.selection.selectAll()

// Unselect all
table.selection.unselectAll()

// Toggle all
table.selection.toggleAll()

// Check state
table.selection.isAllSelected.value // boolean
table.selection.isMixed.value       // boolean (some but not all)

Expansion

Basic Expansion

const table = createDataTable({ items, columns })

// Expand/collapse rows
table.expansion.expand(1)
table.expansion.collapse(1)
table.expansion.toggle(1)

// Check state
table.expansion.isExpanded(1) // boolean
console.log(table.expansion.expandedIds) // Set<ID>

Single Expansion

const table = createDataTable({
  items,
  columns,
  expandMultiple: false,
})

table.expansion.expand(1)
table.expansion.expand(2) // Collapses row 1

Bulk Expansion

// Expand all visible items
table.expansion.expandAll() // Requires expandMultiple: true

// Collapse all (across all pages)
table.expansion.collapseAll()

Grouping

Basic Grouping

const table = createDataTable({
  items,
  columns,
  groupBy: 'department',
})

const groups = table.grouping.groups.value
// [
//   { key: 'Engineering', value: 'Engineering', items: [...] },
//   { key: 'Design', value: 'Design', items: [...] },
// ]

Group Controls

// Open/close groups
table.grouping.open('Engineering')
table.grouping.close('Engineering')
table.grouping.toggle('Engineering')

// Check state
table.grouping.isOpen('Engineering') // boolean

// Bulk operations
table.grouping.openAll()
table.grouping.closeAll()

Auto-Open Groups

const table = createDataTable({
  items,
  columns,
  groupBy: 'department',
  enroll: true, // All groups open initially
})

Adapters

Client Adapter (Default)

Performs filtering, sorting, and pagination client-side:
import { createDataTable, ClientAdapter } from '@vuetify/v0'

const table = createDataTable({
  items,
  columns,
  adapter: new ClientAdapter(), // Default
})

Server Adapter

Delegates to server for data operations:
import { createDataTable, ServerAdapter } from '@vuetify/v0'

const table = createDataTable({
  items: ref([]),
  columns,
  adapter: new ServerAdapter({
    total: 1000,
    fetch: async ({ search, sortBy, page, itemsPerPage }) => {
      const response = await api.fetchUsers({ search, sortBy, page, itemsPerPage })
      return response.data
    },
  }),
})

// Adapter manages loading/error states
console.log(table.loading.value) // boolean
console.log(table.error.value)   // Error | null

Virtual Adapter

For large datasets with virtual scrolling:
import { createDataTable, VirtualAdapter } from '@vuetify/v0'

const table = createDataTable({
  items: largeDataset,
  columns,
  adapter: new VirtualAdapter({
    itemHeight: 48,
    overscan: 5,
  }),
})

Context Pattern

Use dependency injection for global table access:
import { createDataTableContext } from '@vuetify/v0'

export const [useUserTable, provideUserTable, userTable] = createDataTableContext({
  namespace: 'app:users',
  items: users,
  columns,
})

// Parent component
provideUserTable()

// Child component
const table = useUserTable()
table.sort.toggle('name')

TypeScript

Type-Safe Columns

interface User {
  id: number
  name: string
  email: string
}

const columns: DataTableColumn<User>[] = [
  { key: 'name', title: 'Name', sortable: true }, // ✓ Valid
  { key: 'invalid', title: 'Invalid' },           // ✗ Type error
]

Custom Item Value

interface Product {
  sku: string
  name: string
  price: number
}

const table = createDataTable<Product>({
  items: products,
  columns,
  itemValue: 'sku', // Use SKU as identifier
})

Build docs developers (and LLMs) love