TypeScript Setup

Configure TypeScript for type-safe ZenGrid development with current public APIs.

ZenGrid ships TypeScript declarations with @zengrid/core. Use the exported Grid class for runtime code and type imports for interfaces.

Imports

imports.ts
import { Grid, type ColumnDef, type GridOptions, type GridEvents } from '@zengrid/core';
import type { CellRange, FilterModel, SortState } from '@zengrid/core';
import '@zengrid/core/dist/styles.css';
💡 Tip

Use type imports for interfaces and event payload types so bundlers only keep runtime values that are actually needed.

Typed Options

GridOptions is container-agnostic. Pass the DOM container as the first Grid constructor argument.

grid-options.ts
import { Grid, type ColumnDef, type GridOptions } from '@zengrid/core';
const columns: ColumnDef[] = [
{ field: 'id', header: 'ID', width: 80, sortable: true },
{ field: 'name', header: 'Name', width: 220, sortable: true, filterable: true },
{ field: 'department', header: 'Department', width: 180, filterable: true },
];
const options: GridOptions = {
rowCount: 2,
colCount: columns.length,
rowHeight: 36,
colWidth: columns.map((column) => column.width ?? 140),
columns,
enableSelection: true,
enableMultiSelection: true,
selectionType: 'range',
enableKeyboardNavigation: true,
overscanRows: 10,
overscanCols: 5,
};
const grid = new Grid(document.getElementById('grid')!, options);
grid.setData([
[1, 'Alice', 'Engineering'],
[2, 'Bob', 'Sales'],
]);
grid.render();

Mapping Typed Records

The frontend data path uses any[][]. Keep your application records typed, then map them into row arrays for the grid.

records-to-rows.ts
interface Employee {
id: number;
name: string;
email: string;
active: boolean;
}
const employees: Employee[] = [
{ id: 1, name: 'Alice', email: 'alice@example.com', active: true },
{ id: 2, name: 'Bob', email: 'bob@example.com', active: false },
];
const columns: ColumnDef[] = [
{ field: 'id', header: 'ID', width: 80 },
{ field: 'name', header: 'Name', width: 200 },
{ field: 'email', header: 'Email', width: 260 },
{ field: 'active', header: 'Active', width: 100, renderer: 'checkbox' },
];
const rows = employees.map((employee) => [
employee.id,
employee.name,
employee.email,
employee.active,
]);
grid.setData(rows);

Event Types

GridEvents contains the event payload map. You can either let TypeScript infer payloads from grid.on() or name the payload type explicitly.

events.ts
type SelectionChange = GridEvents['selection:change'];
grid.on('cell:click', ({ cell, value, nativeEvent }) => {
console.log(cell.row, cell.col, value, nativeEvent.button);
});
grid.on('cell:doubleClick', ({ cell }) => {
console.log('double clicked', cell);
});
grid.on('selection:change', (event: SelectionChange) => {
console.log(event.ranges);
});
grid.on('edit:commit', ({ cell, oldValue, newValue }) => {
console.log('edited', cell, oldValue, newValue);
});
grid.on('sort:change', ({ sortState }) => {
console.log(sortState);
});
grid.on('filter:change', ({ filterState }) => {
console.log(filterState);
});

Common Types

common-types.ts
const range: CellRange = {
startRow: 0,
startCol: 0,
endRow: 4,
endCol: 2,
};
const sortState: SortState[] = [
{ column: 1, direction: 'asc', sortIndex: 0 },
];
const filterState: FilterModel[] = [
{
column: 2,
conditions: [{ operator: 'contains', value: '@example.com' }],
logic: 'AND',
},
];
grid.sort.setState(sortState);
grid.filter.setState(filterState);

State Persistence

Use the namespaced state API for snapshots.

state-persistence.ts
const snapshot = grid.state.getSnapshot();
localStorage.setItem('gridState', JSON.stringify(snapshot));
const saved = localStorage.getItem('gridState');
if (saved) {
grid.state.applySnapshot(JSON.parse(saved));
}

TypeScript Configuration

tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true
}
}

Reusable Helper

typed-helper.ts
function createEmployeeGrid(container: HTMLElement, employees: Employee[]) {
const grid = new Grid(container, {
rowCount: employees.length,
colCount: columns.length,
rowHeight: 36,
colWidth: columns.map((column) => column.width ?? 140),
columns,
});
grid.setData(
employees.map((employee) => [
employee.id,
employee.name,
employee.email,
employee.active,
])
);
return grid;
}

Next Steps