Data display
Editable grid
A spreadsheet-style editable grid.
Import
import { EditableGrid } from '@the_viveksingh/vivek-ui'An editable order table
Click a cell and use the arrow keys. Enter or F2 edits, typing replaces, Escape cancels. The whole grid is one tab stop, not one per cell.
Click a cell, then use the arrow keys. Enter or F2 edits; typing replaces. The whole grid is a single tab stop.
const [rows, setRows] = useState(lines)
<EditableGrid
data={rows}
label="Order lines"
getRowKey={(row) => row.id}
columns={[
{ key: 'sku', header: 'SKU', width: '7rem' },
{ key: 'product', header: 'Product', editable: true },
{
key: 'qty',
header: 'Qty',
editable: true,
numeric: true,
// Returning undefined rejects the edit - that is the whole validation API.
parse: (input) => (Number(input) >= 0 ? Number(input) : undefined),
},
{
key: 'price',
header: 'Unit price',
editable: true,
numeric: true,
// Displays formatted, edits raw. Conflating the two corrupts the value.
render: (row) => money(row.price),
format: (row) => String(row.price),
parse: (input) => Number(input.replace(/[^0-9.]/g, '')),
},
]}
// Nothing is mutated for you: the grid reports, your state decides.
onCellChange={({ rowIndex, columnKey, value }) =>
setRows((rows) =>
rows.map((row, i) => (i === rowIndex ? { ...row, [columnKey]: value } : row)),
)
}
/>`Read only
readOnly overrides the columns, for a permissions-gated view.
<EditableGrid data={rows} columns={columns} label="Order lines" readOnly />Props
Generated from the package's own type declarations, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
data required | readonly Row[] | — | The data. Named `data` to match DataTable — tabular row sets are always `data`. |
columns required | readonly EditableColumn<Row>[] | — | — |
label required | string | — | Required. A grid with no accessible name is unusable with a screen reader. |
onCellChange | (change: CellChange<Row>) => void | — | Fired when an edit is committed. Nothing is mutated for you — apply it to your state. |
getRowKey | (row: Row, index: number) => string | number | — | Stable row identity. Defaults to the index, which breaks if data reorder. |
readOnly | boolean | — | Blocks editing everywhere, whatever the columns say. |
height | string | number | — | Fixed height turns on scrolling with a sticky header. |
Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.
Rendering
EditableGrid declares 'use client' because it needs state, effects or event handlers. Importing it into a Server Component creates a client boundary at this component — everything above it stays on the server.