Editing
Enable inline cell editing with built-in editors, validation, events, and current APIs.
ZenGrid editing is enabled per column. Users start editing with double-click or keyboard
interaction, and the grid emits edit:* events as edits move through their lifecycle.
Enable Editing
import { Grid, type ColumnDef } from '@zengrid/core';import '@zengrid/core/dist/styles.css';
const rows = [ ['Ada Lovelace', 36, true, 'active'], ['Grace Hopper', 85, true, 'active'], ['Katherine Johnson', 101, false, 'archived'],];
const columns: ColumnDef[] = [ { field: 'name', header: 'Name', width: 180, editable: true, editor: 'text', }, { field: 'age', header: 'Age', width: 90, editable: true, editor: 'number', editorOptions: { min: 0, max: 120 }, }, { field: 'active', header: 'Active', width: 100, editable: true, editor: 'checkbox', }, { field: 'status', header: 'Status', width: 140, editable: true, editor: 'select', editorOptions: { options: ['active', 'pending', 'archived'] }, },];
const grid = new Grid(container, { rowCount: rows.length, colCount: columns.length, rowHeight: 36, colWidth: columns.map((column) => column.width ?? 140), columns, enableSelection: true,});
grid.setData(rows);grid.render(); ℹ Info
Columns are read-only until you set editable: true. Editor-specific configuration belongs in editorOptions.
Built-In Editors
textnumbercheckboxdatetimedatetimedate-rangeselect
Editing Events
grid.on('edit:start', ({ cell, value }) => { console.log('Editing started:', cell, value);});
grid.on('edit:commit', ({ cell, oldValue, newValue }) => { console.log('Value committed:', cell, oldValue, newValue); saveCell(cell.row, cell.col, newValue);});
grid.on('edit:cancel', ({ cell }) => { console.log('Editing cancelled:', cell);});
grid.on('edit:end', ({ cell, cancelled }) => { console.log('Editing ended:', cell, cancelled);});Custom Editors
Custom editors can be supplied directly on a column as a CellEditor instance.
import type { CellEditor, EditorParams } from '@zengrid/core';
class UppercaseEditor implements CellEditor<string> { private input!: HTMLInputElement;
init(container: HTMLElement, value: string, params: EditorParams): void { this.input = document.createElement('input'); this.input.value = value ?? ''; this.input.addEventListener('input', () => { this.input.value = this.input.value.toUpperCase(); params.onChange?.(this.input.value); }); container.appendChild(this.input); }
getValue(): string { return this.input.value; }
focus(): void { this.input.focus(); this.input.select(); }
destroy(): void { this.input.remove(); }
isValid(): boolean { return this.input.value.trim().length > 0; }}
const columns: ColumnDef[] = [ { field: 'code', header: 'Code', width: 120, editable: true, editor: new UppercaseEditor(), },];Programmatic Editing
Most apps should rely on user interaction and edit:* events. If you need to start,
commit, or cancel editing from your own UI, use the plugin API behind a small adapter.
import type { CellRef } from '@zengrid/core';
const api = grid.getGridApi();
const startEdit = api.getMethod('editing', 'startEdit') as | ((cell: CellRef) => void) | undefined;const commitEdit = api.getMethod('editing', 'commitEdit') as | ((value?: unknown) => void) | undefined;const cancelEdit = api.getMethod('editing', 'cancelEdit') as | (() => void) | undefined;const getActive = api.getMethod('editing', 'getActive') as | (() => CellRef | null) | undefined;
startEdit?.({ row: 0, col: 1 });
if (getActive?.()) { commitEdit?.();}
cancelEdit?.();Production Notes
- Persist committed values in your application state or backend from
edit:commit. - Validate again on the server; client validation is a user-experience feature.
- Avoid sharing one stateful custom editor instance across multiple columns unless that is intentional.