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

editable-grid.ts
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

  • text
  • number
  • checkbox
  • date
  • time
  • datetime
  • date-range
  • select

Editing Events

editing-events.ts
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.

custom-editor.ts
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.

programmatic-editing.ts
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.