<script setup lang="ts">
import { ref } from "vue";
import {
Badge,
DataGrid,
Input,
type DataGridColumn,
type DataGridSort,
} from "@dicehub/kappa";
type RecordRow = { id: string; name: string; owner: string; status: string };
const rows = ref<RecordRow[]>([]);
const query = ref("");
const selected = ref<string[]>([]);
const sorting = ref<DataGridSort[]>([]);
const rowToValue = (row: RecordRow) => row.id;
const columns: DataGridColumn<RecordRow>[] = [
{ id: "name", header: "Name", accessorKey: "name", pinned: "start", width: 220 },
{ id: "owner", header: "Owner", accessorKey: "owner", width: 160 },
{ id: "status", header: "Status", accessorKey: "status", width: 120 },
];
</script>
<template>
<DataGrid
v-model:query="query"
v-model:selected-value="selected"
v-model:sorting="sorting"
:columns="columns"
:rows="rows"
:row-to-value="rowToValue"
selection-mode="multiple"
paginated
>
<template #toolbar>
<Input v-model="query" aria-label="Search records" />
<DataGrid.ColumnVisibility />
</template>
<template #cell-status="{ value }"><Badge>{{ value }}</Badge></template>
<template #footer><DataGrid.Pagination /></template>
</DataGrid>
</template>Installation
import { DataGrid } from "@dicehub/kappa";
// Or use the component entry point:
import { DataGrid } from "@dicehub/kappa/components/data-grid";Data Grid uses TanStack Table and TanStack Virtual internally. Its public columns, state, events, and slots are Kappa types.
Usage
Use Data Grid when the application owns interactive data operations. Use Table for static semantic tables and small custom layouts.
Each row needs a stable value. Define columns once, outside render loops. Named cell-* slots support badges, buttons, links, and application-owned inputs without coupling the grid to a data model.
Replace the rows array when data changes, for example rows.value = rows.value.map(updateRow). Changing a field or one array element in place is not supported because the row engine caches accessor values.
Standard mode keeps natural row height. Virtual mode targets large flat data sets, requires a fixed rowHeight, and shows a small loader while its visible row window changes. Do not use multiline cells in virtual mode.
Composition
DataGrid.Root <div>
├── toolbar slot (optional)
│ ├── application controls
│ └── DataGrid.ColumnVisibility (optional)
├── viewport
│ └── semantic table with grid keyboard behavior
│ ├── sortable and resizable headers
│ └── standard or virtual rows
└── footer slot (optional)
└── DataGrid.Pagination (optional)
DataGrid.Context exposes the same reactive state to custom controls.DataGrid.Root renders the grid and provides reactive state to its slots. Put DataGrid.ColumnVisibility in the toolbar slot and DataGrid.Pagination in the footer slot. Use DataGrid.Context when a custom control needs the same state.
Examples
100,000 rows
Virtual mode keeps a small, overscanned row window in the DOM. It requires a fixed row height and a constrained viewport height.
<DataGrid
:columns="columns"
:rows="oneHundredThousandRows"
:row-to-value="rowToValue"
mode="virtual"
height="32rem"
:row-height="28"
:overscan="10"
compact
/>Server-controlled state
This deterministic server simulation owns search, sorting, paging, and the total row count. No live service is required.
<DataGrid
v-model:page="page"
v-model:page-size="pageSize"
v-model:sorting="sorting"
v-model:query="query"
:columns="columns"
:rows="currentServerPage"
:row-count="totalCount"
:row-to-value="rowToValue"
manual-filtering
manual-pagination
manual-sorting
paginated
@page-change="loadPage"
@sorting-change="loadPage"
@query-change="loadPage"
/>Row-click selection
Keep selection without a leading checkbox column. Clicking a row toggles its selected state.
<DataGrid
v-model:selected-value="selected"
:columns="columns"
:rows="rows"
:row-to-value="rowToValue"
selection-mode="multiple"
:show-selection-column="false"
/>Compact
Compact mode reduces the row rhythm for information-dense applications.
<DataGrid compact :columns="columns" :rows="rows" :row-to-value="rowToValue" />Multiline rows
Standard mode keeps native table flow, so rich cells can increase the row height.
<DataGrid :columns="columns" :rows="rows" :row-to-value="rowToValue">
<template #cell-name="{ row }">
<div><strong>{{ row.name }}</strong><p>{{ row.description }}</p></div>
</template>
</DataGrid>Loading, empty, and error
Use complete asynchronous states without replacing the grid shell.
<DataGrid :columns="columns" :rows="[]" :row-to-value="rowToValue" loading />
<DataGrid :columns="columns" :rows="[]" :row-to-value="rowToValue" />
<DataGrid :columns="columns" :rows="[]" :row-to-value="rowToValue" :error="loadError" />Client and server behavior
- Client mode applies query matching, column filters, sorting, and page slicing to
rows. - Manual flags keep the matching operation in the application. Pass the current page plus the full
rowCount. - Paging is one-based in Kappa. The adapter handles engine indexes internally.
- Selection uses stable row values and remains valid when the current page changes.
- Set
showSelectionColumntofalsefor row-click selection without checkboxes. UseselectionMode="none"when selection is not needed. - Column resizing is enabled by default. Drag a resize edge, use Left or Right Arrow from its keyboard focus, or press Enter to restore its initial width. Shift changes the keyboard step.
Accessibility
The component uses semantic table elements with grid behavior. Give every instance an accessible name. Arrow keys move between cells. Home and End move across a row. Control plus Home or End moves to the first or last cell. Page Up and Page Down move by one viewport. Enter or F2 enters an interactive cell; Escape returns focus to the cell. Tab leaves the grid instead of visiting every cell control.
Sorting buttons expose aria-sort. Selection uses labeled Kappa checkboxes. Loading sets aria-busy; failures use an alert.
API Reference
DataGrid.Root
| Prop | Type | Default | Description |
|---|---|---|---|
rows | readonly T[] | — | Rows for the current client data set or server page. |
columns | DataGridColumn<T>[] | — | Kappa-owned column definitions. |
rowToValue | (row: T) => string | — | Stable row identifier used for selection and virtualization. |
direction | "ltr" | "rtl" | inherited | Pointer and keyboard resize direction. CSS direction is detected when omitted. |
mode | "standard" | "virtual" | "standard" | Natural-height or fixed-row virtual rendering. |
selectionMode | "none" | "single" | "multiple" | "none" | Row selection behavior. |
showSelectionColumn | boolean | true | Show the leading checkbox column when selection is enabled. |
sorting / defaultSorting | DataGridSort[] | [] | Controlled sorting or its uncontrolled initial value. |
selectedValue / defaultSelectedValue | string[] | [] | Controlled selection or its uncontrolled initial value. |
query / filters | string / Record<string, unknown> | "" / {} | Client filter state. Use manualFiltering for server data. |
paginated | boolean | false | Enable page slicing and pagination context. |
page / pageSize / rowCount | number | 1 / 25 / rows.length | One-based controlled paging state. |
manualFiltering / manualSorting / manualPagination | boolean | false | Keep the matching data operation in the application. |
height / rowHeight / overscan | CSS height / number / number | "28rem" / 36 / 8 | Virtual viewport geometry. |
loading / error | boolean / unknown | false / undefined | Asynchronous state. |
Columns
DataGridColumn<T> contains id, header, accessorKey or accessor, and optional width, minimum, maximum, alignment, sorting, filtering, search, resizing, hiding, formatting, and pinning settings.
Slots
toolbar, footer, loading, empty, error, header, header-{id}, cell, and cell-{id}.
Events
Each controlled state emits update:* plus a named change event. Activation emits cellActivate or rowActivate. Selection change includes selected values and the matching loaded rows.
Exports
DataGrid, DataGridRoot, DataGridPagination, DataGridColumnVisibility, DataGridContext, and Kappa-owned public types.