createDataTable
A composable data table that composes v0 primitives into a complete data pipeline with sorting, filtering, pagination, selection, expansion, and grouping.
Usage
Pass items and columns to get a fully reactive data table with search, sort, and pagination ready to use.
import { createDataTable } from '@vuetify/v0'
const table = createDataTable({
items: users,
columns: [
{ key: 'name', title: 'Name', sortable: true, filterable: true },
{ key: 'email', title: 'Email', sortable: true, filterable: true },
{ key: 'role', title: 'Role', sortable: true },
],
})
// Search
table.search('john')
console.log(table.query.value) // 'john'
// Sort — toggle cycles: none → asc → desc → none
table.sort.toggle('name')
// Paginate
table.pagination.next()
// Select rows
table.selection.toggle('user-1')| Name | Role | |
|---|---|---|
| Alice Johnson | alice@example.com | Admin |
| Bob Smith | bob@example.com | Editor |
| Carol Davis | carol@example.com | Viewer |
| Dan Wilson | dan@example.com | Editor |
| Eve Martinez | eve@example.com | Admin |
Adapters
Adapters control the data pipeline strategy. Pass one via the adapter option.
| Adapter | Pipeline | Use Case |
|---|---|---|
| ClientAdapter | filter → sort → paginate | Default. All processing client-side |
| ServerAdapter | pass-through | API-driven. Server handles filter/sort/paginate |
| VirtualAdapter | filter → sort → (no paginate) | Large lists rendered with createVirtual |
ClientAdapter (default)
All processing happens client-side. No constructor options — just use createDataTable without an adapter option.
Behavior:
Resets to page 1 on filter or sort changes
totalreflects the sorted item countNo
loadingorerrorstate (synchronous pipeline)
import { createDataTable, ClientAdapter } from '@vuetify/v0'
const table = createDataTable({
items: users,
columns,
adapter: new ClientAdapter(), // default — not required
})ServerAdapter
Pass-through adapter for API-driven tables. The server handles all filtering, sorting, and pagination — the client only renders what it receives.
Constructor options:
| Option | Type | Required | Description |
|---|---|---|---|
total | MaybeRefOrGetter<number> | Yes | Total item count on the server (drives pagination) |
loading | MaybeRefOrGetter<boolean> | No | Loading state (e.g., from useFetch) |
error | MaybeRefOrGetter<Error | null> | No | Error state from API calls |
Behavior:
Resets to page 1 on filter or sort changes
allItems,filteredItems,sortedItems, anditemsall point to the same source (no client-side processing)Exposes
loadinganderrorviatable.loadingandtable.error
import { createDataTable, ServerAdapter } from '@vuetify/v0'
const table = createDataTable({
items: serverItems,
columns,
adapter: new ServerAdapter({
total: totalCount,
loading: isLoading,
error: fetchError,
}),
})
// Watch query/sort/page to trigger API calls
watch(
[table.query, table.sort.columns, table.pagination.page],
() => fetchData()
)VirtualAdapter
Client-side filtering and sorting without pagination slicing. All sorted items are returned for use with createVirtual at the rendering layer.
Behavior:
No constructor options — instantiate with
new VirtualAdapter()Resets on filter or sort changes
No
loadingorerrorstate
import { createDataTable, VirtualAdapter, createVirtual } from '@vuetify/v0'
const table = createDataTable({
items: largeDataset,
columns,
adapter: new VirtualAdapter(),
})
// Wrap table.items with createVirtual for rendering
const virtual = createVirtual(table.items, { itemHeight: 40 })Features
Sorting
Toggle sort cycles through directions. Configure with mandate and firstSortOrder.
const table = createDataTable({
items,
columns: [
{ key: 'name', sortable: true },
{ key: 'age', sortable: true, sort: (a, b) => Number(a) - Number(b) },
],
mandate: true, // asc → desc → asc (never clears)
firstSortOrder: 'desc', // First click sorts descending
sortMultiple: true, // Enable multi-column sort
})
table.sort.toggle('name')
table.sort.direction('name') // 'asc' | 'desc' | 'none'
table.sort.priority('name') // 0-based index, or -1
table.sort.columns.value // [{ key: 'name', direction: 'asc' }]
table.sort.order // ['name'] — multi-sort priority array
table.sort.reset() // Clear all sort stateFiltering
Search filters across all filterable columns. Use per-column filter for custom logic.
const table = createDataTable({
items,
columns: [
{ key: 'name', filterable: true },
{ key: 'status', filterable: true, filter: (value, query) => {
return String(value).toLowerCase() === query.toLowerCase()
}},
],
})
table.search('active')Selection
Control row selection with the selectStrategy option.
| Strategy | Behavior |
|---|---|
'single' | Only one row selected at a time |
'page' | selectAll/toggleAll operate on visible page (default) |
'all' | selectAll/toggleAll operate on all filtered items |
const table = createDataTable({
items,
columns,
selectStrategy: 'page',
itemSelectable: 'canSelect', // Disable selection for rows where canSelect is falsy
})
table.selection.toggle('row-1')
table.selection.isSelected('row-1') // true
table.selection.isSelectable('row-1') // true (based on itemSelectable)
table.selection.toggleAll()
table.selection.isAllSelected.value // true
table.selection.isMixed.value // falseExpansion
Expand rows to reveal detail content.
const table = createDataTable({
items,
columns,
expandMultiple: false, // Only one row expanded at a time
})
table.expansion.toggle('row-1')
table.expansion.isExpanded('row-1') // true
table.expansion.expandAll()
table.expansion.collapseAll()Grouping
Group rows by a column value.
const table = createDataTable({
items,
columns,
groupBy: 'department',
enroll: true, // Auto-open all groups
})
table.grouping.groups.value // [{ key: 'Engineering', value: 'Engineering', items: [...] }]
table.grouping.toggle('Engineering')
table.grouping.isOpen('Engineering')
table.grouping.openAll()
table.grouping.closeAll()Reactivity
| Property | Reactive | Notes |
|---|---|---|
items | Computed — final visible items | |
allItems | Computed — raw unprocessed items | |
filteredItems | Computed — items after filtering | |
sortedItems | Computed — items after filter + sort | |
query | ShallowRef — current search query (readonly) | |
sort.columns | Computed — current sort entries | |
pagination.page | ShallowRef — current page | |
pagination.items | Computed — visible page buttons | |
selection.isAllSelected | Computed — all in scope selected | |
selection.isMixed | Computed — some but not all selected | |
grouping.groups | Computed — grouped items | |
total | Computed — total row count | |
loading | Computed — adapter loading state | |
error | Computed — adapter error state |
Examples
Server Adapter
A data table backed by a simulated API. The ServerAdapter delegates all filtering, sorting, and pagination to the server — the client only renders what it receives.
File breakdown:
| File | Role |
|---|---|
ServerTable.vue | Table with loading state, search, sort, and pagination |
columns.ts | Column definitions |
api.ts | Simulated server with fetchUsers() that filters/sorts/paginates a dataset |
Key patterns:
ServerAdapterreceivestotalandloadingrefs so the table knows the full dataset size without holding it client-sideA
watchon[table.query, table.sort.columns, table.pagination.page]triggersfetchUsers()whenever the user interactsThe simulated API applies search, sort, and pagination server-side, returning only the current page of results
| Name | Department |
|---|
Grouping, Selection & Custom Sort
A grouped table with row selection, custom numeric sort, and salary range filtering. Rows with active: false cannot be selected.
File breakdown:
| File | Role |
|---|---|
FeaturesTable.vue | Grouped table with checkboxes, collapsible groups, and status badges |
columns.ts | Columns with custom sort (numeric) and filter (range queries like >100000) |
data.ts | Employee dataset with departments, salaries, and active status |
Key patterns:
groupBy: 'department'groups rows automatically —enroll: trueopens all groups on creationtable.grouping.isOpen(key)checks visibility,toggle(key)flips ititemSelectable: 'active'disables checkboxes for inactive employeesmandate: trueensures a sort column is always active (never clears to unsorted)
| Name | Department | Salary | Status | |
|---|---|---|---|---|
| − Engineering (4) | ||||
| Alice Johnson | Engineering | $120,000 | Active | |
| Bob Smith | Engineering | $95,000 | Active | |
| Grace Kim | Engineering | $110,000 | Active | |
| Jack Brown | Engineering | $105,000 | Active | |
| − Design (2) | ||||
| Carol Davis | Design | $88,000 | Inactive | |
| Dan Wilson | Design | $92,000 | Active | |
| − Marketing (2) | ||||
| Eve Martinez | Marketing | $78,000 | Active | |
| Frank Lee | Marketing | $82,000 | Active | |
| − Sales (2) | ||||
| Henry Chen | Sales | $75,000 | Inactive | |
| Iris Park | Sales | $85,000 | Active | |
Virtual Scrolling
A table with 1,000 rows rendered through createVirtual. The VirtualAdapter skips pagination — all filtered/sorted items are passed directly to the virtual scroller.
File breakdown:
| File | Role |
|---|---|
VirtualTable.vue | Virtual-scrolled table with sticky header and sort controls |
columns.ts | Column definitions with custom numeric sort for the score column |
data.ts | Generator that creates 1,000 sample user records |
Key patterns:
VirtualAdapterperforms client-side filter and sort but returns all items (no pagination slicing)createVirtual(table.items, { itemHeight: 40 })handles virtualization at the rendering layerThe sticky
<thead>stays visible while scrolling through virtual rowsStats show rendered vs. filtered vs. total counts to demonstrate the virtual window
| Name | Score | |
|---|---|---|
Functions
createDataTable
(options: DataTableOptions<T>) => DataTableContext<T>Creates a data table instance with sort controls, selection, and an adapter-driven data pipeline. Must be called inside a component `setup()` or a Vue effect scope. Calling at module scope in SSR environments causes request state leakage.
createDataTableContext
(_options: DataTableContextOptions<T>) => ContextTrinity<DataTableContext<T>>Creates a data table context with dependency injection support.
useDataTable
(namespace?: string) => DataTableContext<T>Returns the current data table context from dependency injection.
Options
itemValue
KeysOfType<T, ID>Property used as row identifier. Must resolve to a string or number value.
Default: 'id'
selectStrategy
SelectStrategySelection strategy: 'single' selects one row, 'page' operates on visible items, 'all' operates on all filtered items.
Default: 'page'
Properties
grouping
DataTableGrouping<T>Row grouping controls. When groupBy is not set, `groups` returns an empty array.
Methods
Benchmarks
Every operation is profiled across multiple dataset sizes to measure real-world throughput. Each benchmark is assigned a performance tier—good, fast, blazing, or slow—and groups are scored by averaging their individual results so you can spot bottlenecks at a glance. This transparency helps you make informed decisions about which patterns scale for your use case. Learn more in the benchmarks guide.