Skip to content

DataTable

DataTable is a table for reading and comparing rows. Use it for directories, search results, and audit logs where native table semantics and row selection matter. Choose DataGrid when every cell must support keyboard navigation and editing.

A table for reading and comparisonUse search, sorting, and selection, then inspect the same UI by host.
UsersCompare teams, roles, and account status · 8 shown
OverviewSearch, sorting, and selection share one projection.
User informationAccount activity
No matching users
Select a column heading to sort · Shift-select a row range · Select all matching 0 shown · 0 selected

The Code tab switches the same UI between Vue compound components, DOM bindings for existing HTML, and renderer-free Core APIs.

Search and sort

Repeatedly activate a heading to cycle ascending, descending, and off. Search and sort update one query and request a new view instead of mutating mounted DOM rows. The same UI therefore works with both in-memory and server-backed data.

Sorting and filteringChange a heading or search value and observe a new query-backed view.
UsersCompare teams, roles, and account status · 8 shown
Query state{"sort":[],"filters":[]}
User informationAccount activity
No matching users
Select a column heading to sort · Shift-select a row range · Select all matching 0 shown · 0 selected
  • SortTrigger records a column and comparator in the query.
  • FilterControl records a global or column filter.
  • A client source evaluates that query locally; a remote source serializes it for HTTP or RPC.

See async data sources for a complete request, cancellation, failure, and retry flow.

Select rows and all matching results

Individual checkboxes, Shift ranges, and selecting every row matched by the current query share one selection contract. The header checkbox exposes false, mixed, and true for none, some, and all matching rows.

Range and all-matching selectionSelect rows, extend a range with Shift, or select every matching result.
UsersCompare teams, roles, and account status · 8 shown
Selection state{"kind":"explicit-rows","rowIDs":[]}
User informationAccount activity
No matching users
Select a column heading to sort · Shift-select a row range · Select all matching 0 shown · 0 selected

SelectionControl inherits the current Body row. BulkSelectionControl with all-matching stores the query revision and exclusions rather than every unloaded ID. Set name for native form submission and override value only when it differs from the row ID.

Multi-level headers and edit intent

Do not specify a header-row depth. Bind leaf headers with column, and only bind a spanning group with header. Tabular derives depth, colspan, rowspan, and accessibility metadata from the schema.

Header hierarchy and edit intentInspect calculated header spans and native input commit intent.
UsersCompare teams, roles, and account status · 8 shown
StructureTwo header levels · native input commit intent
User informationAccount activity
No matching users
Select a column heading to sort · Shift-select a row range · Select all matching 0 shown · 0 selected

Editor does not persist data. It converts a native input commit into a typed command; the application owns validation, storage, and optimistic updates. Use DataGrid when a cell cursor and edit mode are central.

Column visibility, pinning, and size

Order, visibility, and start/end pinning are portable semantic state. Pixel width belongs to the DOM or Vue host. This keeps Core platform-independent while each rendered surface can use real measurements.

Column size and visibilitySee how resize handles and semantic column state keep separate responsibilities.
UsersCompare teams, roles, and account status · 8 shown
Column state{"hidden":[],"pinnedStart":[]}
User informationAccount activity
No matching users
Select a column heading to sort · Shift-select a row range · Select all matching 0 shown · 0 selected

ColumnResizeHandle supports pointer and keyboard input and respects min/max limits. Column visibility and pinning update controller columnState, so they can be persisted or controlled by the application.

Connect data and render request states

useDataTable({ source }) executes requests, and the returned controller exposes status, error, reload, and cancel. Tabular does not prescribe the spinner, empty state, error copy, or retry control. It keeps the last accepted view while exposing the new request separately.

SituationRead this state
Initial requeststatus === 'loading' with no accepted view
Sorting requestExisting rows remain while a request is pending
Empty resultAccepted view has rows.length === 0
Failure and retryerror, reload()

See async data sources for the complete flow.

Preserve row types in Vue

createDataTableComponents(table) creates a component namespace bound to the source response schema. A Body slot therefore preserves the inferred rows[].cells type, and Cell inherits the row ID from Body.

template
<DataTable.Body v-slot="{ row }">
  <DataTable.Cell column="name">{{ row.cells.name }}</DataTable.Cell>
  <DataTable.Cell column="role">{{ row.cells.role }}</DataTable.Cell>
</DataTable.Body>

Body owns normal row iteration. Use <DataTable.Body manual> and explicit DataTable.Row only for low-level rendering such as a virtual window.

Find a public part

GoalVue partDOM/Core equivalent
Table and nameRoot, Captionnative table / controller projection
HeadersHeader, HeaderRow, ColumnHeaderheader attributes / schema
QuerySortTrigger, FilterControlbind functions / set-query event
RowsBody, Row, Cellelement registration / projection rows
SelectionSelectionControl, BulkSelectionControlcheckbox binding / selection event
GroupsDisclosuredisclosure binding / expansion event
EditingEditoreditor binding / commit command
Column sizeColumnResizeHandleresize binding / host size state

See Vue composition, DOM composition, and shared contracts for installation paths and full state shapes.

Released under the MIT License.