Data Grid
ProA spreadsheet in your app: select a cell or a range, type over it, paste a block from Excel or Sheets and undo it in one step. Calm rows at rest; the column rules arrive when the grid has focus.
Installation#
Usage#
Pass columns and rows, and take the next rows back in onRowsChange. DataGridViewport is the scrolling grid and needs a height; DataGridStatusBar is optional. For a table people read rather than edit, use Data Table.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import { DataGrid, DataGridViewport, DataGridStatusBar } from "@/components/ui/data-grid"
<DataGrid columns={columns} rows={rows} onRowsChange={setRows}>
<DataGridViewport />
<DataGridStatusBar />
</DataGrid>Column types#
The type picks the editor and how a value reads, sorts, copies and pastes. Select a cell and press Enter to edit it.
text: an input in the cell.numberandcurrency: right-aligned in tabular figures; pasted text like €1,240.50 reads as 1240.5.select: a badge; Enter opens the options, typing filters them.date: Enter opens the calendar; typing a date also works. Stored as YYYY-MM-DD.checkbox: Space or a click toggles it in place.- A column with
valueis computed and read-only, like Value here.
Keyboard#
Spreadsheet keys, with Ctrl on Windows and Cmd on a Mac.
| Arrows | Move one cell. With Shift, grow the range. |
| Ctrl/Cmd + Arrows | Jump to the edge of the data. With Shift, grow the range to it. |
| Home, End | Start or end of the row. With Ctrl/Cmd, the first or last cell. |
| Page Up, Page Down | One screen of rows. |
| Tab, Shift + Tab | Next or previous cell; at the row's end, focus leaves the grid. |
| Enter, F2, double-click | Edit in place: the arrows move the caret. |
| Type a character | Replace the cell: the arrows commit and move. |
| Enter, Shift + Enter | While editing: commit and move down or up. |
| Ctrl/Cmd + Enter | While editing: write the value into every selected cell. |
| Escape | Cancel the edit, or collapse the range and clear the copy marker. |
| Delete, Backspace | Clear the selected cells. |
| Space | Toggle a checkbox cell. |
| Shift + Space, Ctrl + Space | Select the row or the column. |
| Ctrl/Cmd + A | Select every cell. |
| Ctrl/Cmd + C, X, V | Copy, cut and paste as TSV and HTML. |
| Ctrl/Cmd + Z, Shift + Z or Y | Undo, redo. |
| Arrow Up on the first row | Move into the header: Enter sorts, Alt + Arrows resize. |
Row numbers and gridlines#
rowNumbers adds a frozen number column; click a number to select the row. gridlines="all" keeps the column rules on at rest, for finance sheets.
Validation#
Return a message from validate to reject a value: the editor stays open, shakes once and shows the message under the cell. A value already in the data that fails shows a corner mark. Try a letter or a negative number in Stock.
Saving changes#
onRowsChange fires once per action with patches, so a paste of 200 cells is one call and one undo step. Send the patches to your server instead of diffing the rows.
- Edit, paste or press Delete to see the patches.
Adding rows and columns#
onAddRow shows a New row row at the end and moves to it; onAddColumn shows a + at the end of the header. The grid never invents data: you append it.
Large data#
Only the rows in view are mounted, so ten thousand rows scroll and select like twelve. Ctrl+End jumps to the last cell; Ctrl+Shift+End selects to it and the status bar sums the lot.
Read-only#
readOnly keeps selection, copy and the status bar, and writes nothing. Pair it with gridlines="rows" and the comfortable density for a report.
API reference#
DataGrid
The root: owns the rows, the columns, the selection, the undo history and the sort, and shares them with the parts through Context. With no children it renders a DataGridViewport. Forwards native <div> props; every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| columns* | DataGridColumn<TRow>[] | - | The column definitions; see the Column table below. |
| rows | TRow[] | - | Controlled rows. Pair with onRowsChange. |
| defaultRows | TRow[] | - | Uncontrolled rows. |
| onRowsChange | (rows, { changes, source }) => void | - | Fires once per action with the next rows and the cell patches behind them: { rowId, columnId, previous, value }. source is edit, paste, cut, clear, fill, undo or redo. |
| getRowId | (row, index) => string | - | A stable id per row, used in the patches. Defaults to the row's index. |
| readOnly | boolean | - | Select and copy only. |
| rowNumbers | boolean | false | A pinned row-number column; click a number to select the row. |
| gridlines | "focus" | "rows" | "all" | "focus" | focus draws the column rules while the grid has focus; rows never; all always. |
| density | "comfortable" | "compact" | - | Rows of 40px or 32px. Defaults to the surrounding DensityProvider. |
| sort, defaultSort, onSortChange | { columnId, direction: "asc" | "desc" } | null | - | The column sort, controlled or not. |
| onColumnWidthsChange | (widths: Record<string, number>) => void | - | Fires when a resize or an auto-fit ends, to persist the widths. |
| onAddRow | () => void | - | Shows a trailing New row row. Append the row here; the grid moves to it. |
| onAddColumn | () => void | - | Shows a + cell at the end of the header. Append the column here. |
| labels | Partial<DataGridLabels> | - | Every string the grid shows or announces, for translation. |
| aria-label | string | - | Names the grid for screen readers. |
DataGridViewport
The scroll box, role="grid": the sticky header, the rows in view, the selection overlay and the editor. Give it a height. Copy, cut and paste are handled here. Forwards native <div> props.
| Data attribute | Description |
|---|---|
| data-scrolled-x | While scrolled sideways; shows the shadow on the frozen columns' edge. |
| CSS variable | Description |
|---|---|
| --dg-w{n} | The width of column n, written on resize. |
| --dg-v | The column rule colour; transparent at rest with gridlines="focus". |
| --dg-fill | The range fill: 5% ink, 9% on dark themes. |
| --surface | Read for the ground under sticky headers and frozen columns; falls back to --background. |
DataGridStatusBar
Count, sum and average of the selection, live and in tabular figures. Currency columns sum in their currency. Forwards native <div> props.
DataGridColumn
The column definition passed in columns.
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | - | Unique id; also the row field read when there is no accessorKey or value. |
| header* | string | - | The header label; also names the column for screen readers. |
| accessorKey | keyof TRow | - | The row field this column reads and writes. Defaults to id. |
| value | (row: TRow) => unknown | - | Derives the value instead of reading a field; the column is read-only. |
| type | "text" | "number" | "currency" | "date" | "select" | "checkbox" | "text" | Sets the editor, the alignment, the display format and how pasted text is read. |
| width | number | 150 | Starting width in px. Users can drag the header edge or double-click it to fit. |
| minWidth, maxWidth | number | 56, 640 | Bounds for resizing. |
| pin | "left" | - | Freezes the column at the left edge; pinned columns render first. |
| readOnly | boolean | - | Selectable and copyable, never written. |
| sortable | boolean | true | Shows the sort control in the header. |
| options | { value: string; label: string }[] | - | select columns: the choices, shown as badges in the cell. |
| currency | string | "USD" | currency columns: an ISO 4217 code. |
| align | "start" | "center" | "end" | - | Defaults to end for numbers, center for checkboxes, start otherwise. |
| format | (value, row) => string | - | Display text, and what a copy writes. |
| parse | (text, row) => unknown | - | Turns typed or pasted text into a value. |
| validate | (value, row) => string | null | - | Return a message to reject a value: the editor stays open, shakes and shows it under the cell. Rejected cells are skipped on paste. |
| cell | ({ value, row, rowIndex }) => ReactNode | - | Custom cell content. |