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
  rows={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 rows={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
rows requiredreadonly Row[]
columns requiredreadonly EditableColumn<Row>[]
label requiredstringRequired. A grid with no accessible name is unusable with a screen reader.
onCellChange(change: CellChange<Row>) => voidFired when an edit is committed. Nothing is mutated for you — apply it to your state.
getRowKey(row: Row, index: number) => string | numberStable row identity. Defaults to the index, which breaks if rows reorder.
readOnlybooleanBlocks editing everywhere, whatever the columns say.
heightstring | numberFixed height turns on scrolling with a sticky header.
classNamestring
idstring

Rendering