Your First Grid

Step-by-step tutorial building a full-featured data grid with columns, renderers, and events.

Build a full-featured grid with the current Grid constructor, typed columns, array data, renderers, and events.

Step 1: Container

index.html
<div id="grid-container"></div>
<script type="module" src="/src/main.ts"></script>
styles.css
#grid-container {
width: 100%;
height: 600px;
border: 1px solid #d1d5db;
}
Warning

The grid container must have explicit width and height before you call render().

Step 2: Columns

columns.ts
import type { ColumnDef } from '@zengrid/core';
export const columns: ColumnDef[] = [
{ field: 'id', header: 'ID', width: 80, sortable: true },
{ field: 'name', header: 'Name', width: 200, sortable: true, editable: true },
{ field: 'email', header: 'Email', width: 260, filterable: true },
{ field: 'department', header: 'Department', width: 180, filterable: true },
{ field: 'salary', header: 'Salary', width: 120, renderer: 'currency', sortable: true },
{ field: 'active', header: 'Active', width: 100, renderer: 'checkbox', editable: true },
];

Step 3: Data

Keep application records typed, then map them to a row matrix for grid.setData().

data.ts
export interface Employee {
id: number;
name: string;
email: string;
department: string;
salary: number;
active: boolean;
}
export const employees: Employee[] = [
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering', salary: 95000, active: true },
{ id: 2, name: 'Bob Smith', email: 'bob@example.com', department: 'Sales', salary: 75000, active: true },
{ id: 3, name: 'Charlie Brown', email: 'charlie@example.com', department: 'Marketing', salary: 68000, active: false },
];
export const rows = employees.map((employee) => [
employee.id,
employee.name,
employee.email,
employee.department,
employee.salary,
employee.active,
]);

Step 4: Create The Grid

main.ts
import { Grid } from '@zengrid/core';
import '@zengrid/core/dist/styles.css';
import { columns } from './columns';
import { rows } from './data';
const grid = new Grid(document.getElementById('grid-container')!, {
columns,
rowCount: rows.length,
colCount: columns.length,
rowHeight: 40,
colWidth: columns.map((column) => column.width ?? 140),
enableSelection: true,
enableMultiSelection: true,
selectionType: 'range',
enableKeyboardNavigation: true,
});
grid.setData(rows);
grid.render();

Step 5: Custom Renderer

renderers.ts
import type { CellRenderer } from '@zengrid/core';
export const currencyRenderer: CellRenderer = {
render(element, { value }) {
element.textContent = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(Number(value));
},
update(element, params) {
this.render(element, params);
},
destroy(element) {
element.textContent = '';
},
};

Register the renderer before the first render.

main.ts
import { currencyRenderer } from './renderers';
grid.registerRenderer('currency', currencyRenderer);
grid.render();

Step 6: Events And APIs

events.ts
grid.on('cell:click', ({ cell, value }) => {
console.log(`Clicked row ${cell.row}, column ${cell.col}:`, value);
});
grid.on('cell:doubleClick', ({ cell }) => {
console.log('Double clicked', cell);
});
grid.on('selection:change', ({ ranges }) => {
console.log('Selected ranges:', ranges);
});
grid.on('edit:commit', ({ cell, newValue }) => {
rows[cell.row][cell.col] = newValue;
});
grid.on('sort:change', ({ sortState }) => {
console.log('Sort state:', sortState);
});
grid.on('filter:change', ({ filterState }) => {
console.log('Filter state:', filterState);
});
grid.sort.toggle(1);
grid.filter.set(2, 'contains', '@example.com');
grid.columns.autoFitAll();
Info

Use current event names such as cell:doubleClick, edit:commit, sort:change, filter:change, and column:resize.

Cleanup

cleanup.ts
window.addEventListener('beforeunload', () => {
grid.destroy();
});

In framework wrappers, call destroy() from the component unmount hook.

Complete Example

complete-example.ts
import { Grid, type CellRenderer, type ColumnDef } from '@zengrid/core';
import '@zengrid/core/dist/styles.css';
const columns: ColumnDef[] = [
{ field: 'id', header: 'ID', width: 80, sortable: true },
{ field: 'name', header: 'Name', width: 200, sortable: true, editable: true },
{ field: 'email', header: 'Email', width: 260, filterable: true },
{ field: 'salary', header: 'Salary', width: 120, renderer: 'currency' },
];
const rows = [
[1, 'Alice', 'alice@example.com', 95000],
[2, 'Bob', 'bob@example.com', 75000],
[3, 'Charlie', 'charlie@example.com', 68000],
];
const currencyRenderer: CellRenderer = {
render(element, { value }) {
element.textContent = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(Number(value));
},
update(element, params) {
this.render(element, params);
},
destroy(element) {
element.textContent = '';
},
};
const grid = new Grid(document.getElementById('grid-container')!, {
columns,
rowCount: rows.length,
colCount: columns.length,
rowHeight: 40,
colWidth: columns.map((column) => column.width ?? 140),
enableSelection: true,
selectionType: 'range',
});
grid.registerRenderer('currency', currencyRenderer);
grid.setData(rows);
grid.render();

Next Steps

Learn about TypeScript Setup for stricter typing and reusable helpers.