KQ-1311 · DATA/GRID

Data Grid

A data grid that keeps pace with the hand: rows arrive after a skeleton in a cascade on glide (row by row, on a diagonal wave of cells, or in one fade) and replay from the side you paged toward, a heading press morphs its sort glyph between a double chevron and an arrow on snap while the rows on the page glide to their new seats, and columns resize from a grip that follows the finger 1:1, rubber-bands past its limits and springs back on snap with the release velocity. Shift-click selects a range drawn as one band that stretches from the anchor on snap, editable cells open an editor that lifts out of the cell and land their new value on flick, and the header and first columns stay pinned while the body scrolls. It is a real role="grid" with one roving cell: arrows move, Page keys turn the page, Space selects, Shift with an arrow extends the range, Enter sorts a heading or edits a cell, and Shift with Left or Right resizes a column.

KQ-1311 · DATA/GRID

Receivables

Loading invoices

Arrow keys move between cells. Space selects a row, Shift with an arrow extends the selection. Enter sorts a heading or edits a cell. Shift with Left or Right resizes a heading's column, Shift with Enter fits it to its contents. Page Up and Page Down turn the page.

Customer
Invoice
Status
Amount
Due
Owner
Region

 

loading 42 invoices

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/data-grid

Props

density
"compact" | "regular" | "roomy"
default "regular"
Row height and type: 32, 40 or 48px rows. Heights glide when it changes.
cascade
"rows" | "wave" | "none"
default "rows"
How a page arrives after the skeleton or a page turn: rows rising in order, cells on a diagonal wave from the top-left, or one fade.
stripes
boolean
default false
Paint alternate rows, so a wide row is easy to follow across. The pinned cells share the stripe.
columns
DataGridColumn[]
default defaultDataGridColumns
The columns in order: id, header, kind (text, number, money, date, status), width and its limits, align, sortable, editable, mono, relative dates, status options and tones, and format, validate and parse functions. The first is the row header and stays pinned.
rows / defaultRows
DataGridRow[]
default defaultDataGridRows
The records, each with an id and a field per column. Controlled or uncontrolled; the default is 42 seeded invoices.
onRowsChange
(rows: DataGridRow[]) => void
default —
Fires with every row after an edit is committed, or reverted after a failed save.
onCellEdit
(edit: DataGridEdit) => void | Promise<unknown>
default —
Fires when an edit is committed, with the row, column, new value and previous value. Return a promise to show the cell saving; a rejection puts the old value back.
sort / defaultSort
{ columnId: string; direction: "asc" | "desc" } | null
default null
The sort, or null for the rows' own order. Controlled or uncontrolled.
onSortChange
(sort: DataGridSort | null) => void
default —
Fires from the heading press or key that changed the sort: ascending, descending, then off.
selected / defaultSelected
string[]
default []
The selected row ids. Controlled or uncontrolled.
onSelectedChange
(ids: string[]) => void
default —
Fires from the click, Shift-click or key that changed the selection.
page / defaultPage
number
default 0
The page shown, from 0. Controlled or uncontrolled.
onPageChange
(page: number) => void
default —
Fires from the button or key that turned the page.
pageSize
number
default 8
Rows per page.
maxHeight
number
default 360
Tallest the body grows, in px, before it scrolls under its pinned header.
actions
DataGridAction[]
default []
Buttons offered in the footer while rows are selected: id, label, an optional 14px icon and tone.
onAction
(actionId: string, rowIds: string[]) => void
default —
One of the actions was pressed, with the selected row ids.
onColumnResize
(columnId: string, width: number) => void
default —
Fires when a column's width settles after a drag, a double-click fit or a key.
now
number | Date
default defaultDataGridNow
Today, for relative dates such as "in 3d" and "5d ago".
itemLabel
{ one: string; other: string }
default { one: "row", other: "rows" }
What one row is called in counts and announcements: "3 invoices selected".
locale
string
default "en-US"
The locale for numbers and money.
currency
string
default "USD"
The currency for money columns.
status
"ready" | "loading" | "error"
default "ready"
Whether the rows have arrived. Loading shows a skeleton of one page; going to ready plays the arrival.
onRetry
() => void
default —
Try again was pressed after the rows failed to load.
emptyLabel
string
default "Nothing here yet"
What an empty grid says.
title
string
default "Receivables"
The heading over the grid.
label
string
default —
The grid's accessible name. Defaults to the title.
sound
boolean
default false
Play a click for sorts, toggles, page turns and saves, and a tick per row joining a range and per 16px of resize.
disabled
boolean
default false
Keeps the grid readable and navigable but refuses sorting, selecting, editing, resizing and paging.
className
string
default —
Classes for the outer panel.