Skip to content

Migrating from v2 to v3

This guide covers the breaking changes in @toolbox-web/grid v3. Every removed symbol was @deprecated throughout the v2.x line, so if you kept up with IDE deprecation warnings you may have little to change.

The main focus of this version was to improve core isolation from plugins/features to improve bundle size on both core and adapters. The adapter that benefitted most from this was the grid-angular package which halved its bundle size.

The biggest behavioural change: the shell (header bar, toolbar content, and tool panels) was extracted into a built-in ShellPlugin and is no longer auto-registered. A bare <tbw-grid> now renders no shell chrome, which lets the shell tree-shake out of the core bundle when you don’t use it.

Activate the shell explicitly using one of these forms:

// Side-effect import registers the feature, then enable it in gridConfig.
import '@toolbox-web/grid/features/shell';
grid.gridConfig = {
features: { shell: true }, // or: { shell: { /* ShellConfig */ } }
};

The deprecated grid-element shell delegates (grid.openToolPanel(...), grid.registerHeaderContent(...), grid.getToolPanels(), etc.) have been removed. Reach the shell through the plugin instance instead:

const shell = grid.getPluginByName('shell');
shell?.openToolPanel('filters');
shell?.registerHeaderContent(/* ... */);

The shell config types are no longer re-exported from the core entry point. Import them from the plugin subpath:

// v2 (removed core re-exports)
// import type { ShellConfig, ToolPanelConfig } from '@toolbox-web/grid';
// v3
import type { ShellConfig, ToolPanelConfig } from '@toolbox-web/grid/plugins/shell';
v2 (removed)v3 (replacement)
DGEvents, DGEventNameString literals typed against DataGridEventMap (or keyof DataGridEventMap)
PluginEvents, PluginEventNameString literals typed against DataGridEventMap
@toolbox-web/grid/plugins/reorder-rows (RowReorderPlugin)@toolbox-web/grid/plugins/row-drag-drop (RowDragDropPlugin)
RowDragDropConfig.canDragRowRowDragDropConfig.canDrag
RowDragDropConfig.canMoveRowDragDropConfig.canDrag(row, index) — no direct 1:1; the old (row, from, to, direction) signature is gone (veto now runs on drag start)
ServerSideConfig.cacheBlockSizeServerSideConfig.pageSize
ServerSidePlugin.getNodeCount()ServerSidePlugin.getTotalNodeCount()
ServerSidePlugin.isLoaded(index)ServerSidePlugin.isNodeLoaded(index)
PinnedRowsConfig.position, showRowCount, showSelectedCount, showFilteredCount, aggregationRows, customPanelsPinnedRowsConfig.slots[]
PinnedRowsPlugin.addPanel / removePanel / addAggregationRow / removeAggregationRowConfigure slots[] declaratively
// v2 (removed)
import { DGEvents, PluginEvents } from '@toolbox-web/grid';
grid.on(DGEvents.SORT_CHANGE, (detail) => { /* ... */ });
grid.on(PluginEvents.FILTER_CHANGE, (detail) => { /* ... */ });
// v3 — string literals are type-checked against DataGridEventMap
grid.on('sort-change', (detail) => { /* ... */ });
grid.on('filter-change', (detail) => { /* ... */ });
// v2 (removed legacy fields)
new PinnedRowsPlugin({ showRowCount: true, aggregationRows: [/* ... */] });
// v3 — the unified slots[] API
new PinnedRowsPlugin({
slots: [
{ position: 'bottom', aggregators: { price: 'sum' }, label: 'Total' },
],
});

The default new PinnedRowsPlugin() still seeds the familiar bottom info bar (row count, filtered count, selected count), so the no-argument case is unchanged.

Angular adapter (@toolbox-web/grid-angular)

Section titled “Angular adapter (@toolbox-web/grid-angular)”

The Angular adapter’s v2 major ships alongside grid core v3 and applies the same core-isolation principle: feature directives, editor/filter base classes, and inject helpers moved out of the package’s main entry into per-feature subpaths (@toolbox-web/grid-angular/features/*). This is what roughly halves the adapter’s baseline bundle — the main entry now pulls in only the core Grid directive plus shared types, and every feature tree-shakes away unless you import its subpath.

The main entry (@toolbox-web/grid-angular) still exports the core surface: Grid, TbwGridColumn, TbwGridHeader, TbwGridToolButtons, TbwRenderer, GridColumnView, injectGrid, provideGrid / provideGridIcons / provideGridTypeDefaults, and the shared types (GridConfig, ColumnConfig, CellEditor, CellRenderer, FilterPanel, CellCommitEvent, RowCommitEvent). Everything feature-specific moved. Most apps hit three mechanical changes.

1. Import base classes & inject helpers from their feature subpath

Section titled “1. Import base classes & inject helpers from their feature subpath”
v2 symbol(s)Import from
BaseGridEditor, BaseGridEditorCVA, BaseOverlayEditor, TbwEditor, GridFormArray, GridColumnEditor, GridLazyForm@toolbox-web/grid-angular/features/editing
BaseFilterPanel, injectGridFiltering, FilterConfig@toolbox-web/grid-angular/features/filtering
injectGridSelection@toolbox-web/grid-angular/features/selection
GridToolPanel, GridHeaderContent, GridToolbarContent@toolbox-web/grid-angular/features/shell
// v1
import { BaseGridEditor, BaseOverlayEditor, TbwEditor, GridToolPanel } from '@toolbox-web/grid-angular';
// v2
import { BaseGridEditor, BaseOverlayEditor, TbwEditor } from '@toolbox-web/grid-angular/features/editing';
import { GridToolPanel } from '@toolbox-web/grid-angular/features/shell';

2. Import a feature directive for every feature you bind in a template

Section titled “2. Import a feature directive for every feature you bind in a template”

In v1 the core Grid directive silently owned the feature inputs ([filtering], [editing], [selection], …). In v2 each binding is owned by its own directive in the feature subpath. If your <tbw-grid> template binds a feature, add that directive to the standalone component’s imports (importing it also registers the vanilla bridge as a side effect). Forget it and Angular fails the build with NG8002: Can't bind to 'filtering' since it isn't a known property of 'tbw-grid'.

import { Grid } from '@toolbox-web/grid-angular';
import { GridFilteringDirective } from '@toolbox-web/grid-angular/features/filtering';
import { GridEditingDirective } from '@toolbox-web/grid-angular/features/editing';
@Component({
imports: [Grid, GridFilteringDirective, GridEditingDirective /* … */],
template: `<tbw-grid [filtering]="true" [editing]="editConfig" />`,
})
export class MyComponent {}

Binding → directive → subpath (each subpath also exports the directive):

Template bindingDirectiveSubpath
[filtering], (filterChange)GridFilteringDirectivefeatures/filtering
[editing], (cellCommit), (rowCommit), (cellCancel), …GridEditingDirectivefeatures/editing
[selection], (selectionChange)GridSelectionDirectivefeatures/selection
[contextMenu], (contextMenuOpen)GridContextMenuDirectivefeatures/context-menu
[visibility]GridVisibilityDirectivefeatures/visibility
[rowDragDrop], (rowMove), (rowDrop), …GridRowDragDropDirectivefeatures/row-drag-drop
[tree], (treeExpand)GridTreeDirectivefeatures/tree
[masterDetail], (detailExpand)GridMasterDetailDirectivefeatures/master-detail
[multiSort]GridMultiSortDirectivefeatures/multi-sort
[pinnedRows]GridPinnedRowsDirectivefeatures/pinned-rows
[pinnedColumns]GridPinnedColumnsDirectivefeatures/pinned-columns
[groupingRows], (groupToggle), (groupExpand), …GridGroupingRowsDirectivefeatures/grouping-rows
[groupingColumns]GridGroupingColumnsDirectivefeatures/grouping-columns
[reorderColumns], (columnMove)GridReorderColumnsDirectivefeatures/reorder-columns
[clipboard], (copy), (paste)GridClipboardDirectivefeatures/clipboard
[export], (exportComplete)GridExportDirectivefeatures/export
[print], (printStart), (printComplete)GridPrintDirectivefeatures/print
[responsive], (responsiveChange)GridResponsiveDirectivefeatures/responsive
[stickyRows]GridStickyRowsDirectivefeatures/sticky-rows
[serverSide]GridServerSideDirectivefeatures/server-side
[pivot]GridPivotDirectivefeatures/pivot
[tooltip]GridTooltipDirectivefeatures/tooltip
[undoRedo], (undo), (redo)GridUndoRedoDirectivefeatures/undo-redo
[columnVirtualization]GridColumnVirtualizationDirectivefeatures/column-virtualization

Prefer configuring a feature in gridConfig (features / plugins) over a template binding when you don’t need the two-way input — then no directive import is required at all.

3. reorder-rowsrow-drag-drop, and shell delegates

Section titled “3. reorder-rows → row-drag-drop, and shell delegates”

Mirroring the core rename (RowReorderPluginRowDragDropPlugin), the Angular subpath and its template binding were renamed:

import '@toolbox-web/grid-angular/features/reorder-rows';
import { GridRowDragDropDirective } from '@toolbox-web/grid-angular/features/row-drag-drop';
<tbw-grid [reorderRows]="true" />
<tbw-grid [rowDragDrop]="true" />

The removed shell element-delegates (see Shell API access) apply to Angular too — reach the shell through the plugin instead of the element:

this.grid.element()?.toggleToolPanel();
this.grid.element()?.getPluginByName('shell')?.toggleToolPanel();

4. Enable the shell for features that render in a tool panel

Section titled “4. Enable the shell for features that render in a tool panel”

The shell being opt-in is the change most likely to bite an Angular grid at runtime (not build time), because v1 auto-registered it. Any feature that renders into a tool panel — most commonly visibility (its column list) — declares a hard dependency on the shell and throws TBW020 if the shell is absent.

There is no [shell] template directive (the shell subpath only exports the content directives GridToolPanel, GridHeaderContent, GridToolbarContent). Enable the shell through gridConfig, and register its feature with the usual side-effect import:

import '@toolbox-web/grid-angular/features/shell';
features: {
shell: true,
visibility: true,
// …
},

This holds even when the dependent feature is enabled through a template directive (<tbw-grid [visibility]="true">): the directive contributes the visibility feature, but the shell still has to come from gridConfig.features.

React (@toolbox-web/grid-react) & Vue (@toolbox-web/grid-vue)

Section titled “React (@toolbox-web/grid-react) & Vue (@toolbox-web/grid-vue)”

Like Angular, the React and Vue adapters ship a v2 major alongside grid core v3 and now require @toolbox-web/grid@^3.0.0 as a peer dependency. Their changes are identical, so the steps below apply to both — substitute <DataGrid …> (React) or <TbwGrid …> (Vue) as appropriate.

Mirroring the core rename, the reorderRows prop and its features/reorder-rows subpath were removed. Use rowDragDrop and the features/row-drag-drop subpath:

import '@toolbox-web/grid-react/features/reorder-rows';
import '@toolbox-web/grid-react/features/row-drag-drop';
<DataGrid reorderRows />
<DataGrid rowDragDrop />

The adapter-specific pinnedRows.customPanels array (which accepted a framework-component render function) was removed in favour of the unified slots[] API, matching the core pinned-rows change. Move each custom panel into a slots[] entry:

pinnedRows={{
customPanels: [{ id: 'total', position: 'bottom', render: () => <Total /> }],
slots: [{ position: 'bottom', render: () => <Total /> }],
}}

The deprecated ssr prop and the exported SSRProps type were removed from both adapters. The prop was a no-op — features register via SSR-safe side-effect imports, and <tbw-grid> is a custom element that hydrates normally on the client regardless of the flag. Delete any ssr prop and SSRProps import; no replacement is needed.

import type { SSRProps } from '@toolbox-web/grid-react';
<DataGrid ssr rows={rows} columns={columns} />
<DataGrid rows={rows} columns={columns} />

If you need real server-side rendering (Next.js / Remix / Nuxt / Vike / Astro), open an issue describing your hydration requirements.

The shell being opt-in applies to React and Vue as well. Enable it with the shell prop (or via gridConfig.features.shell), and reach the shell instance through the grid element ref rather than removed element delegates — see Shell API access. Features that render into a tool panel (e.g. visibility) throw TBW020 at runtime when the shell is absent.

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