EditableGrid

Language:

8 rows, 6 columns — basic example with all cell types. ID column is readonly.

IDNameDepartmentStart DateSalaryActive
EMP-001
Alice Johnson
Engineering
2022-03-15
$95,000
EMP-002
Bob Smith
Design
2021-07-01
$82,000
EMP-003
Carol Williams
Marketing
2023-01-10
$75,000
EMP-004
David Brown
Sales
2020-11-20
$68,000
EMP-005
Eva Martinez
Human Resources
2022-09-05
$72,000
EMP-006
Frank Lee
Finance
2021-04-12
$88,000
EMP-007
Grace Chen
Engineering
2023-06-18
$102,000
EMP-008
Henry Davis
Design
2022-12-01
$79,000

Click any cell to edit. Readonly columns (ID, SKU) cannot be edited. Use Tab to move between cells, Enter to move down, Escape to cancel.

EditableGrid

An Excel-like spreadsheet component for editing structured datasets. Supports typed cells (text, number, date, select, checkbox, textarea), readonly columns for fixed identifiers, keyboard navigation, per-cell validation, and row add/delete.

Usage

import { EditableGrid } from "@/components/ui/editable-grid"
import type { EditableColumnDef } from "@/components/ui/editable-grid"

const columns: EditableColumnDef<Row>[] = [
  { columnId: "id", title: "ID", type: "text", readonly: true },
  { columnId: "name", title: "Name", type: "text", validation: { required: true } },
  { columnId: "department", title: "Dept", type: "select", options: [...] },
  { columnId: "startDate", title: "Start", type: "date" },
  { columnId: "salary", title: "Salary", type: "number", aligned: "right" },
  { columnId: "active", title: "Active", type: "checkbox", aligned: "center" },
]

<EditableGrid<Row>
  columns={columns}
  data={data}
  rowIdKey="id"
  onCellChange={(rowId, columnId, value) => updateData(rowId, columnId, value)}
  onAddRow={() => addEmptyRow()}
  onDeleteRows={(ids) => removeRows(ids)}
  enableAddRow
  enableDeleteRow
  enableRowSelection
  bordered
/>

EditableGrid Props

PropTypeDefaultDescription
columns*EditableColumnDef<TData>[]—Column definitions array.
data*TData[]—The data array. Component is controlled — parent owns the data.
rowIdKey*keyof TData & string—Property name used as unique row identifier (e.g. 'id', 'ssn').
onCellChange(rowId, columnId, newValue) => void—Fired when a cell value is committed.
onRowChange(rowId, updatedRow) => void—Fired with the full updated row after a cell commit.
onAddRow() => void—Fired when the add-row button is clicked.
onDeleteRows(rowIds) => void—Fired when rows are deleted (single or bulk).
enableAddRowbooleanfalseShow the add-row button at the bottom.
enableDeleteRowbooleanfalseShow per-row delete buttons and bulk delete.
enableRowSelectionbooleanfalseShow row selection checkboxes.
language"en" | "pt""en"UI language for labels and validation messages.
borderedbooleanfalseAdd borders to all cells.
compactbooleanfalseReduce cell padding and font size.
accentColorstring—Accent color for checkboxes. Supports color names, #hex, or rgb().
classNamestring—Additional class name for the outer wrapper.

EditableColumnDef Fields

PropTypeDefaultDescription
columnId*keyof TData & string—Data property key for this column.
title*string—Column header label.
type*CellType—"text" | "number" | "date" | "select" | "checkbox" | "textarea"
widthnumber | string—Fixed column width (px or CSS string).
readonlybooleanfalseVisible but not editable (for ID columns).
optionsOptionItem[]—Dropdown options. Required when type is 'select'.
validationValidationRule—Per-cell validation: required, min, max, minLength, maxLength, pattern, custom.
placeholderstring—Placeholder text shown in empty cells and inputs.
formatter(value, row) => ReactNode—Custom display formatter for when the cell is not being edited.
aligned"left" | "center" | "right""left"Text alignment within the cell.

ValidationRule Fields

PropTypeDefaultDescription
requiredboolean—Value must not be empty.
minnumber—Minimum number value.
maxnumber—Maximum number value.
minLengthnumber—Minimum string length.
maxLengthnumber—Maximum string length.
patternRegExp—Regex pattern for text validation.
custom(value, row) => string | null—Custom validation function. Return error message or null.