Data display

Editable grid

A spreadsheet-style editable grid.

Client componentSource

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.

SKU
Product
Qty
Unit price
Total
VK-1001
Annual licence
12
$240.00
$2,880.00
VK-1002
Priority support
3
$1,200.00
$3,600.00
VK-1003
Onboarding workshop
1
$3,500.00
$3,500.00
VK-1004
Extra seats
25
$60.00
$1,500.00
Order total $11,480.00
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.

SKU
Product
Qty
VK-1001
Annual licence
12
VK-1002
Priority support
3
VK-1003
Onboarding workshop
1
VK-1004
Extra seats
25
<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.

Props for EditableGrid
PropTypeDefaultDescription
data requiredreadonly Row[]—The data. Named `data` to match DataTable — tabular row sets are always `data`.
columns requiredreadonly EditableColumn<Row>[]——
label requiredstring—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.
readOnlyboolean—Blocks editing everywhere, whatever the columns say.
heightstring | 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