Skip to content

API Reference

Complete reference for the <tbw-grid> component API.

<tbw-grid></tbw-grid>

The custom element is registered as tbw-grid (Toolbox Web Grid).

PropertyTypeDefaultDescription
rowsT[][]Array of row data objects
columnsColumnConfig[][]Column configuration array
gridConfigGridConfig<T>{}Full configuration object
PropertyTypeDefaultDescription
fitMode'stretch' | 'fixed''stretch'Column sizing strategy
PropertyTypeDefaultDescription
loadingbooleanfalseGrid-level loading state. Customise the indicator via gridConfig.loadingRenderer
columnStateGridColumnState[]-Get/set column widths, order, visibility, sort
PropertyTypeDescription
changedRowsT[]Rows with pending edits (requires EditingPlugin)
sortStateMap<string, SortState>Current sort state per column

The grid supports two forms of declarative HTML configuration:

  1. HTML Attributes — JSON-serialized values on the <tbw-grid> element itself
  2. Light DOM Elements — Child elements nested inside <tbw-grid> for more readable markup

Both approaches can be combined. Light DOM elements take precedence over JSON attributes for the same configuration (e.g., <tbw-grid-column> elements override the columns attribute).

Attributes on the <tbw-grid> element for simple or programmatically-generated configuration:

AttributeMaps to PropertyTypeDescription
rowsrowsJSONRow data as JSON array
columnscolumnsJSONColumn config as JSON array
grid-configgridConfigJSONFull config object as JSON
fit-modefitModestring'stretch' or 'fixed'
<tbw-grid
rows='[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]'
columns='[{"field":"id","header":"ID"},{"field":"name","header":"Name"}]'
fit-mode="stretch"
></tbw-grid>

Some plugins read their own data-* attributes directly from the <tbw-grid> host element when they attach. These are namespaced under data- to avoid colliding with the grid’s own reactive attributes (rows, columns, grid-config, fit-mode, loading). They are read once at attach time — changing them afterward has no effect (use the JSON grid-config attribute or a JS property for reactive updates).

AttributeOwning pluginDescription
data-srcServerSidePluginURL to fetch row data from — enables a zero-JS server-fetched grid. See Server-Side → Declarative data-src.

Child elements inside <tbw-grid> for more readable, template-friendly configuration. Preferred for framework templates (Angular, Vue, etc.) and complex column setups with custom renderers/editors.

Define column configuration declaratively.

AttributeTypeDescription
fieldstringRequired. Data field key
headerstringColumn header text
typestringColumn type. Built-ins: 'string', 'number', 'boolean', 'date', 'select'. Any custom/plugin-registered type string is also accepted.
widthstringColumn width (e.g., "120", "100px", "20%")
min-widthnumberMinimum column width in pixels
sortablebooleanEnable sorting (presence = true)
resizablebooleanEnable resizing (presence = true)
ordernumberInitial column position (0-based index; must be non-negative integer). Columns without order fill positions in declaration order. Only affects initial render; user reorder takes precedence.
editablebooleanEnable editing (presence = true, requires EditingPlugin)
optionsstringSelect options: "value1:Label1,value2:Label2" or "val1,val2"
pinnedstringPin the column to an edge: 'left' / 'start' or 'right' / 'end' (requires PinnedColumnsPlugin).
hiddenbooleanHide the column initially (presence = true; hidden="false" keeps it visible). Requires VisibilityPlugin.
lock-visiblebooleanPrevent the column from being hidden via the visibility panel (presence = true). Requires VisibilityPlugin.

Plugin-contributed attributes (pinned, hidden, lock-visible, editable, <tbw-grid-column-editor>) are read by their owning plugin when it is registered. They set the column’s initial state — subsequent runtime changes (e.g. setColumnVisible(), drag-to-pin) take precedence.

<tbw-grid>
<tbw-grid-column field="id" header="ID" type="number" width="80" order="0"></tbw-grid-column>
<tbw-grid-column field="name" header="Name" sortable resizable order="1"></tbw-grid-column>
<tbw-grid-column field="role" type="select" options="admin:Admin,user:User" order="2"></tbw-grid-column>
</tbw-grid>

Custom view template inside a column. Content is used as the cell renderer.

<tbw-grid-column field="status">
<tbw-grid-column-view>
<span class="badge">{{ value }}</span>
</tbw-grid-column-view>
</tbw-grid-column>

Custom editor template inside a column (requires EditingPlugin).

<tbw-grid-column field="name" editable>
<tbw-grid-column-editor>
<input type="text" />
</tbw-grid-column-editor>
</tbw-grid-column>

Custom header template inside a column.

<tbw-grid-column field="status">
<tbw-grid-column-header>
<strong>Status</strong> <span class="required">*</span>
</tbw-grid-column-header>
</tbw-grid-column>

Configure the shell header bar.

AttributeTypeDescription
titlestringGrid title (left side)
<tbw-grid>
<tbw-grid-header title="Employee Directory">
<tbw-grid-header-content>
<span>20 employees</span>
</tbw-grid-header-content>
</tbw-grid-header>
</tbw-grid>

Custom content in the shell header center area. Must be nested inside <tbw-grid-header>.

<tbw-grid-header title="My Grid">
<tbw-grid-header-content>
<input type="search" placeholder="Search..." />
</tbw-grid-header-content>
</tbw-grid-header>

Container for toolbar buttons (right side of shell header).

<tbw-grid>
<tbw-grid-tool-buttons>
<button class="tbw-toolbar-btn" title="Export">📥</button>
<button class="tbw-toolbar-btn" title="Print">🖨️</button>
</tbw-grid-tool-buttons>
</tbw-grid>

Define a custom tool panel for the sidebar.

AttributeTypeDescription
idstringRequired. Unique panel identifier
titlestringRequired. Panel title in accordion header
iconstringIcon for accordion header (emoji or text)
tooltipstringTooltip for accordion header
ordernumberPanel order priority (lower = first, default: 100)
<tbw-grid>
<tbw-grid-tool-panel id="filters" title="Filters" icon="🔍" order="10">
<div class="filter-panel">
<label>Status: <select>...</select></label>
</div>
</tbw-grid-tool-panel>
</tbw-grid>

Define the detail panel template for master-detail rows. Requires the MasterDetailPlugin.

AttributeTypeDescription
animation'slide' | 'fade' | falsePanel expand/collapse animation (default: 'slide')
show-expand-columnbooleanShow expand/collapse column (default: true)
expand-on-row-clickbooleanExpand when row clicked (default: false)
heightnumber | 'auto'Panel height in pixels or auto (default: 'auto')
<tbw-grid>
<tbw-grid-detail animation="slide" expand-on-row-click="true">
<div class="detail-panel">
<h3>{{ row.name }}</h3>
<p>{{ row.description }}</p>
</div>
</tbw-grid-detail>
</tbw-grid>

See the MasterDetailPlugin documentation for more details.

<tbw-grid-responsive-card> (ResponsivePlugin)
Section titled “<tbw-grid-responsive-card> (ResponsivePlugin)”

Define a custom card template for responsive mode. Requires the ResponsivePlugin.

AttributeTypeDescription
breakpointnumberWidth threshold in pixels for responsive mode
card-row-heightnumber | 'auto'Card height in pixels or auto (default: 'auto')
hidden-columnsstringComma-separated field names to hide in card mode
hide-headerbooleanHide header row in responsive mode (default: true)
debounce-msnumberResize debounce delay in ms (default: 100)
<tbw-grid>
<tbw-grid-responsive-card breakpoint="500" card-row-height="80" hidden-columns="createdAt, updatedAt">
<div class="custom-card">
<strong>{{ row.name }}</strong>
<span>{{ row.email }}</span>
</div>
</tbw-grid-responsive-card>
</tbw-grid>

See the ResponsivePlugin documentation for more details.

import { createGrid, queryGrid } from '@toolbox-web/grid';
// Create a new grid element with configuration
const grid = createGrid<Employee>({
columns: [{ field: 'name' }, { field: 'email' }],
fitMode: 'stretch',
});
document.body.appendChild(grid);
grid.rows = employees;
// Query an existing grid with type safety
const existingGrid = queryGrid<Employee>('#my-grid');
if (existingGrid) {
existingGrid.rows = newData;
}
// Update row data
grid.rows = newData;
// Wait for grid to be ready
await grid.ready();
// Force layout recalculation
await grid.forceLayout();
// Get current configuration (readonly)
const config = await grid.getConfig();

The Row Update API provides ID-based access to individual rows for reading and updating data. This is especially useful when you need to update rows after external changes (e.g., API responses, WebSocket events) without replacing the entire dataset.

The grid automatically determines row IDs using these sources (in order of precedence):

  1. getRowId function in gridConfig (custom ID resolution)
  2. id property on the row object
  3. _id property on the row object
// Configure custom row ID resolution
grid.gridConfig = {
columns: [...],
getRowId: (row) => row.employeeId, // Use a custom field as ID
};
// Get a row's ID
const id = grid.getRowId(row);
console.log(id); // "EMP-123"
// Get a single row by its ID
const employee = grid.getRow('EMP-123');
if (employee) {
console.log(employee.name);
}
// Returns undefined if the row is not found
const missing = grid.getRow('unknown-id');
console.log(missing); // undefined
// Update a single row by ID
grid.updateRow('EMP-123', { status: 'active', salary: 75000 });
// Batch update multiple rows
grid.updateRows([
{ id: 'EMP-123', changes: { status: 'active' } },
{ id: 'EMP-456', changes: { department: 'Engineering' } },
]);
// Specify the update source (default: 'api')
// Valid sources: 'user' | 'cascade' | 'api'
grid.updateRow('EMP-123', { status: 'inactive' }, 'api');
grid.updateRows(updates, 'api');

The cell-change event fires whenever row data is updated via the Row Update API:

grid.on('cell-change', ({ rowId, changes, source, row }) => {
console.log(`Row ${rowId} updated via ${source}:`);
console.log('Changes:', changes); // { status: 'active' }
console.log('Full row:', row); // Complete row object after update
});
// Get all columns with visibility info
const columns = grid.getAllColumns();
// Returns: Array<{ field, header, visible, lockVisible? }>
// Show/hide columns
grid.setColumnVisible('fieldName', false);
grid.toggleColumnVisibility('fieldName');
grid.showAllColumns();
// Check column visibility
const isVisible = grid.isColumnVisible('fieldName');
// Reorder columns
grid.setColumnOrder(['id', 'name', 'email']);
const order = grid.getColumnOrder();

Bulk-edit lifecycle, change tracking, and active-row controls are provided by the Editing plugin and surfaced on the grid instance when the plugin is loaded. See Editing Plugin → Imperative Bulk-Edit API for the full method list and examples.

MethodReturnsDescription
getPluginByName(name)Plugin | undefinedGet plugin instance by name — preferred (type-safe, no import needed)
getPlugin(PluginClass)P | undefinedGet plugin instance by class (requires import)

getPluginByName is type-safe for all built-in plugins via the PluginNameMap interface — TypeScript automatically returns the correct plugin type (e.g., SelectionPlugin for 'selection').

// Preferred — type-safe, no import needed
const selection = grid.getPluginByName('selection');
selection?.selectAll(); // ✅ SelectionPlugin methods are available
// Alternative — get by class (requires plugin import)
import { SelectionPlugin } from '@toolbox-web/grid/plugins/selection';
const sel = grid.getPlugin(SelectionPlugin);
// Check if plugin is registered
if (selection) {
selection.selectAll();
}

The grid uses light DOM — standard CSS targeting elements inside <tbw-grid> works normally (global stylesheets, <style> in <head>, external CSS files).

For programmatic runtime styles, use registerStyles() which injects CSS via adoptedStyleSheets:

// Inject styles programmatically at runtime
grid.registerStyles('my-id', '.my-class { color: blue; }');
// Remove when no longer needed
grid.unregisterStyles('my-id');
// List currently registered style IDs
const styleIds = grid.getRegisteredStyles();

When you set rows, the grid automatically re-applies all active processing — sorting, filtering, grouping — so the data stays consistent. This is the right behavior for data refreshes (e.g., API responses), but not for manual row insertion or removal where you want the new row to stay exactly where you placed it.

insertRow(index, row) and removeRow(index) operate directly on the current view without running the plugin pipeline. The row is also added to / removed from the source data so that the next full pipeline run includes it correctly.

Both methods auto-animate by default ('insert' / 'remove' animation). Pass false as the last argument to skip animation. They return Promises that resolve when the animation completes.

// Insert a row at visible index 3 (auto-animates)
grid.insertRow(3, newEmployee);
// Insert without animation
grid.insertRow(3, newEmployee, false);
// Remove with fade-out animation (default) — await to ensure removal
await grid.removeRow(5);
// Remove immediately, no animation
await grid.removeRow(5, false);

All animation methods return Promises that resolve when the animation completes.

import type { RowAnimationType } from '@toolbox-web/grid';
// Animate a single row by index (await to know when done)
await grid.animateRow(rowIndex, 'change'); // 'insert' | 'change' | 'remove'
// Animate a row by its ID
await grid.animateRowById('EMP-123', 'insert');
// Animate multiple rows at once
await grid.animateRows([0, 1, 2], 'change');
// Grid-level loading
grid.loading = true;
grid.loading = false;
// To customise the loading indicator, configure a renderer at config time:
grid.gridConfig = {
loadingRenderer: (ctx) => {
const el = document.createElement('div');
el.textContent = ctx.size === 'large' ? 'Loading…' : '';
return el;
},
};
// Row-level loading (by row ID, not index)
grid.setRowLoading('emp-123', true);
grid.setRowLoading('emp-123', false);
// Cell-level loading (by row ID and field name)
grid.setCellLoading('emp-123', 'email', true);
grid.setCellLoading('emp-123', 'email', false);
// Get current column state (for saving)
const state = grid.getColumnState();
localStorage.setItem('gridState', JSON.stringify(state));
// Restore column state
const saved = localStorage.getItem('gridState');
if (saved) {
grid.applyColumnState(JSON.parse(saved));
}
// Reset to initial configuration
grid.resetColumnState();

The header bar, toolbar content, and tool panels are owned by the built-in shell plugin. Access it via grid.getPluginByName('shell') rather than the deprecated grid.* element delegates (removed in v3):

const shell = grid.getPluginByName('shell');
// Register a tool panel
shell?.registerToolPanel({
id: 'myPanel',
title: 'My Panel',
icon: '⚙️',
render: (container) => {
container.innerHTML = '<div>Panel content</div>';
},
});
// Open/close/toggle the tool panel sidebar
shell?.openToolPanel();
shell?.closeToolPanel();
shell?.toggleToolPanel();
// Open directly on a specific accordion section (one-click navigation)
shell?.openToolPanel('myPanel');
// Toggle specific accordion sections
shell?.toggleToolPanelSection('myPanel');
// Check panel state
const isOpen = shell?.isToolPanelOpen;
const sections = shell?.expandedToolPanelSections;
// Get registered panels
const panels = shell?.getToolPanels();

Register custom content in the grid’s header bar (above column headers) or toolbar area through the shell plugin:

const shell = grid.getPluginByName('shell');
// Register header content (e.g., a search box)
shell?.registerHeaderContent({
id: 'global-search',
order: 10,
render: (container) => {
const input = document.createElement('input');
input.type = 'search';
input.placeholder = 'Search all columns...';
container.appendChild(input);
},
});
// Unregister header content
shell?.unregisterHeaderContent('global-search');
// Get all registered header content definitions
const headerContents = shell?.getHeaderContents();
// Register toolbar content (e.g., action buttons)
shell?.registerToolbarContent({
id: 'export-buttons',
order: 100,
render: (container) => {
const btn = document.createElement('button');
btn.textContent = 'Export CSV';
btn.className = 'tbw-toolbar-btn';
container.appendChild(btn);
},
});
// Unregister toolbar content
shell?.unregisterToolbarContent('export-buttons');
// Get all registered toolbar content definitions
const toolbarContents = shell?.getToolbarContents();
// Get the default row height in pixels
// For fixed heights: the configured or CSS-measured row height
// For variable heights: the average/estimated row height
const height = grid.defaultRowHeight;

When building custom editors that render outside the grid DOM (overlays, datepickers, dropdowns appended to <body>), use the focus container registry so the grid treats focus inside those elements as “still in the grid”.

// Register an overlay panel so focus moving into it doesn't close the editor
const panel = document.createElement('div');
document.body.appendChild(panel);
grid.registerExternalFocusContainer(panel);
// When the overlay is torn down, unregister it
grid.unregisterExternalFocusContainer(panel);
// Check whether focus is logically inside the grid (own DOM + external containers)
const hasFocus = grid.containsFocus(); // checks document.activeElement
const nodeInGrid = grid.containsFocus(someNode); // checks specific node

Programmatic cell focus and scroll-to-row:

// Focus a cell by column index or field name
grid.focusCell(0, 2); // row 0, column index 2
grid.focusCell(5, 'email'); // row 5, field 'email'
// Read the currently focused cell
const cell = grid.focusedCell;
if (cell) {
console.log(`Row ${cell.rowIndex}, field "${cell.field}"`);
}
// Scroll a row into view (no-op if already visible)
grid.scrollToRow(42);
// Center the row with smooth scrolling
grid.scrollToRow(42, { align: 'center', behavior: 'smooth' });
// Scroll by row ID (requires getRowId or id/_id property)
grid.scrollToRowById('emp-42', { align: 'center' });

ScrollToRowOptions:

OptionTypeDefaultDescription
align'start' | 'center' | 'end' | 'nearest''nearest'Where to position the row
behavior'instant' | 'smooth''instant'Scroll animation

The grid dispatches standard CustomEvents that bubble up the DOM. All events use bubbles: true and composed: true.

Click cells, sort columns, resize columns to see events.

For framework-specific patterns (vanilla TypeScript, React, Vue, Angular) and the camelCase vs kebab-case binding rules, see Common Patterns → Event Handling Recipes.

Five events support preventDefault() to reject the action:

EventPluginWhat Canceling Does
cell-activateCoreSuppresses the default activation — prevents plugins (e.g. inline editing) from acting on the click/keypress
cell-commitEditingReverts the cell to its previous value
row-commitEditingReverts the entire row to pre-edit state
column-moveReorderRejects the column drag-and-drop
row-moveRow ReorderRejects the row drag/keyboard move

cell-activate fires first, before any plugin handles the interaction, and for both pointer clicks and keyboard activation (Enter) — its detail.trigger is 'pointer' or 'keyboard'. Because it covers the keyboard path and is cancelable, prefer it over cell-click (pointer-only) for click-to-open / activate-to-edit patterns that must stay keyboard-accessible.

The tbw-scroll event fires (rAF-batched) on every viewport scroll; the render event fires once per render flush after all plugin afterRender hooks complete. Cookbook patterns — infinite scroll, sticky progress indicators, lazy cell hydration, post-addRow focus, phase-gated render handlers — live in Common Patterns → Event Handling Recipes.

For the typed event payloads, see DataGridEventMap.

The DataGridEventMap is the complete, auto-generated reference for every event the grid dispatches — core events and plugin events alike. It includes detail payload types, descriptions, and code examples, grouped by plugin.

The grid exposes ~80 CSS custom properties for theming. See the Theming guide for the complete, categorised list and the GridIcons interface for the icon variables.

The patterns that show how to use the configuration API live with their respective guides — this section is just a directory:

PatternWhere it lives
Dynamic rowClass / cellClassCore → Row Styling & Cell Styling
Type-level defaults (formatters, renderers, editors)Core → Type-Level Defaults
Row animation (expand/collapse, reorder, insert/remove)Core → Row Animation and the AnimationConfig interface
Shell layout (header, toolbar, tool panels)Shell plugin and the ShellConfig interface
Icon customization (CSS + JS paths)Theming → Icon Customization and the GridIcons interface

The grid implements the W3C WAI-ARIA Grid Pattern out of the box — ARIA roles (grid / row / gridcell / columnheader), aria-rowindex / aria-colindex / aria-rowcount / aria-colcount, aria-sort, aria-selected, aria-readonly, and aria-checked are applied automatically.

Configure accessible naming via gridAriaLabel / gridAriaDescribedBy on GridConfig. Customise screen-reader announcements via A11yConfig.

For the full keyboard shortcut matrix, ARIA reference, screen-reader guidance, focus management, and high-contrast support, see the Accessibility guide.

Modern evergreen browsers — Chrome/Edge 79+, Firefox 63+, Safari 13.1+. No polyfills required. See the Production Checklist for deployment guidance.

AI assistants: For complete API documentation, implementation guides, and code examples for this library, see https://toolboxjs.com/llms-full.txt