Skip to content

Common Patterns

Real-world grids rarely use a single feature in isolation. This guide shows tested combinations that solve everyday requirements.

Goal: Let users sort, filter, and select rows from a large dataset.

import '@toolbox-web/grid';
import '@toolbox-web/grid/features/selection';
import '@toolbox-web/grid/features/filtering';
import '@toolbox-web/grid/features/multi-sort';
import { queryGrid } from '@toolbox-web/grid';
import type { ColumnConfig } from '@toolbox-web/grid';
const grid = queryGrid<Employee>('#grid');
grid.gridConfig = {
columns: [
{ field: 'id', header: 'ID', type: 'number', sortable: true },
{ field: 'name', header: 'Name', sortable: true, filterable: true },
{ field: 'department', header: 'Department', filterable: true },
{ field: 'salary', header: 'Salary', type: 'number', sortable: true },
],
features: {
selection: { mode: 'row', multiSelect: true },
filtering: { debounceMs: 200 },
multiSort: true,
},
};
// React to selected rows
grid.on('selection-change', () => {
const sel = grid.getPluginByName('selection');
const rows = sel?.getSelectedRows<Employee>() ?? [];
console.log('Selected:', rows);
});

Goal: Inline cell editing with full undo/redo support.

import '@toolbox-web/grid/features/editing';
import '@toolbox-web/grid/features/undo-redo';
import '@toolbox-web/grid/features/selection';
grid.gridConfig = {
columns: [
{ field: 'id', header: 'ID', type: 'number' },
{ field: 'name', header: 'Name', editable: true },
{ field: 'email', header: 'Email', editable: true },
{ field: 'active', header: 'Active', type: 'boolean', editable: true },
],
features: {
editing: { editOn: 'dblclick', dirtyTracking: true },
undoRedo: true,
selection: 'cell',
},
getRowId: (row) => row.id, // Required for dirty tracking
};
// Validate before committing
grid.on('cell-commit', (detail, e) => {
const { field, value } = detail;
if (field === 'email' && !value.includes('@')) {
e.preventDefault(); // Reject invalid edit
}
});
// Track dirty state
grid.on('dirty-change', () => {
const editing = grid.getPluginByName('editing');
const dirtyRows = editing?.getDirtyRows() ?? [];
saveButton.disabled = dirtyRows.length === 0;
});

Goal: Group rows by a field and show aggregated values (sum, average, count).

import '@toolbox-web/grid/features/grouping-rows';
import '@toolbox-web/grid/features/selection';
const columns = [
{ field: 'department', header: 'Department' },
{ field: 'name', header: 'Name' },
{
field: 'salary',
header: 'Salary',
type: 'number',
aggregator: 'avg',
formatter: (ctx) => `$${ctx.value.toLocaleString()}`,
},
{
field: 'id',
header: 'Count',
type: 'number',
aggregator: 'count',
},
];
grid.gridConfig = {
columns,
features: {
groupingRows: { groupOn: (row) => [row.department], defaultExpanded: true },
selection: 'row',
},
};

Goal: Let users filter data, select a subset, and export it.

import '@toolbox-web/grid/features/export';
import '@toolbox-web/grid/features/selection';
import '@toolbox-web/grid/features/filtering';
grid.gridConfig = {
columns: [
{ field: 'name', header: 'Name', filterable: true },
{ field: 'email', header: 'Email' },
{ field: 'department', header: 'Department', filterable: true },
],
features: {
selection: { mode: 'row', multiSelect: true },
filtering: true,
export: true,
},
};
// Export button — exports only selected rows (or all if none selected)
exportButton.addEventListener('click', () => {
const exp = grid.getPluginByName('export');
exp?.exportCsv({ selectedOnly: true, fileName: 'employees.csv' });
});

Goal: Expand a row to show related detail records.

import '@toolbox-web/grid/features/master-detail';
grid.gridConfig = {
columns: [
{ field: 'orderId', header: 'Order', type: 'number' },
{ field: 'customer', header: 'Customer' },
{ field: 'total', header: 'Total', type: 'number' },
],
features: {
masterDetail: {
detailRenderer: (ctx) => {
const container = document.createElement('div');
container.style.padding = '1rem';
container.innerHTML = `<strong>Order #${ctx.row.orderId}</strong>`;
// Nested grid for order items
const detail = document.createElement('tbw-grid') as any;
detail.style.height = '200px';
detail.style.display = 'block';
detail.columns = [
{ field: 'item', header: 'Item' },
{ field: 'qty', header: 'Qty', type: 'number' },
{ field: 'price', header: 'Price', type: 'number' },
];
detail.rows = ctx.row.items; // Nested data
container.appendChild(detail);
return container;
},
},
},
};

Goal: Handle 10k+ rows with pinned identifier columns and column virtualization.

import '@toolbox-web/grid/features/pinned-columns';
import '@toolbox-web/grid/features/column-virtualization';
import '@toolbox-web/grid/features/multi-sort';
import '@toolbox-web/grid/features/filtering';
grid.gridConfig = {
columns: [
{ field: 'id', header: 'ID', type: 'number', pinned: 'left', width: 80 },
{ field: 'name', header: 'Name', pinned: 'left', width: 180 },
// ... many more columns
],
features: {
pinnedColumns: true,
columnVirtualization: true,
multiSort: true,
filtering: { debounceMs: 300 },
},
fitMode: 'none', // Let columns use natural widths
};
AI assistants: For complete API documentation, implementation guides, and code examples for this library, see https://raw.githubusercontent.com/OysteinAmundsen/toolbox/main/llms-full.txt