Skip to content

Column Grouping Plugin

The Column Grouping plugin enables visual grouping of columns under shared headers.

import '@toolbox-web/grid/features/grouping-columns';

There are three ways to define column groups, from most to least recommended:

Section titled “Option 1: Feature config columnGroups (Recommended)”

Define everything in one place — groups, renderers, and plugin behavior:

import { queryGrid } from '@toolbox-web/grid';
import '@toolbox-web/grid/features/grouping-columns';
const grid = queryGrid('tbw-grid');
grid.gridConfig = {
columns: [
{ field: 'firstName', header: 'First Name' },
{ field: 'lastName', header: 'Last Name' },
{ field: 'email', header: 'Email' },
{ field: 'department', header: 'Department' },
{ field: 'title', header: 'Title' },
{ field: 'salary', header: 'Salary' },
],
features: {
groupingColumns: {
columnGroups: [
{ header: 'Personal Info', children: ['firstName', 'lastName', 'email'] },
{ header: 'Work Info', children: ['department', 'title', 'salary'] },
],
},
},
};

If your groups are part of a server-provided layout or shared data model, define them on gridConfig.columnGroups:

grid.gridConfig = {
columnGroups: [
{ header: 'Personal Info', children: ['firstName', 'lastName', 'email'] },
{ header: 'Work Info', children: ['department', 'title', 'salary'] },
],
columns: [...],
features: { groupingColumns: true },
};

For simple cases, assign group membership directly on each column. The group string becomes the group id:

grid.gridConfig = {
columns: [
{ field: 'firstName', header: 'First Name', group: 'personal' },
{ field: 'lastName', header: 'Last Name', group: 'personal' },
{ field: 'department', header: 'Department', group: 'work' },
],
features: { groupingColumns: true },
};

You can also pass an object with an explicit label:

{ field: 'firstName', header: 'First Name', group: { id: 'personal', label: 'Personal Info' } }
Show group borders Show borders between column groups

Use groupHeaderRenderer to customize the content of group header cells. The renderer is called once per group, receives a GroupHeaderRenderParams object (including the group id), and can return an HTMLElement, an HTML string, or void to keep the default label.

Since the renderer receives params.id, a single function can differentiate rendering per group:

grid.gridConfig = {
columns: [...],
features: {
groupingColumns: {
columnGroups: [
{ header: 'Personal Info', children: ['firstName', 'lastName', 'email'] },
{ header: 'Work Info', children: ['department', 'title', 'salary'] },
],
groupHeaderRenderer: (params) => {
const icons: Record<string, string> = {
'personal-info': '👤',
'work-info': '💼',
};
const icon = icons[params.id] ?? '📁';
return `${icon} <strong>${params.label}</strong> (${params.columns.length})`;
},
},
},
};

You can also define a renderer directly on individual group definitions. A per-group renderer takes precedence over groupHeaderRenderer for that specific group:

grid.gridConfig = {
columns: [...],
features: {
groupingColumns: {
columnGroups: [
{
header: 'Personal Info',
children: ['firstName', 'lastName', 'email'],
renderer: (params) => `👤 <strong>${params.label}</strong>`,
},
{ header: 'Work Info', children: ['department', 'title', 'salary'] },
],
// Fallback for groups without their own renderer (Work Info in this case)
groupHeaderRenderer: (params) => `<strong>${params.label}</strong>`,
},
},
};
WhatWhereNotes
Group definitionsfeatures.groupingColumns.columnGroupsRecommended — keeps everything in one place
Group definitions (alt)gridConfig.columnGroupsUseful for server-driven layouts
Group membershipcolumns[].groupSimplest — just assigns a column to a group
Custom renderinggroupHeaderRenderer or per-group rendererOn the feature config or group definition
Plugin behaviorshowGroupBorders, lockGroupOrderOn the feature config
  • Plugin options: GroupingColumnsConfigcolumnGroups, groupHeaderRenderer, showGroupBorders, lockGroupOrder
  • Group definition: ColumnGroupDefinitionid (optional), header, children, renderer
  • Renderer params: GroupHeaderRenderParamsid, label, columns, firstIndex, isImplicit
  • Runtime group data: ColumnGroup — computed group objects returned by getGroups()
  • Column config group: string (group ID) or { id: string; label?: string }

The plugin provides these methods on the plugin instance:

const plugin = grid.getPluginByName('groupingColumns');
// Check if grouping is active
plugin.isGroupingActive(); // boolean
// Get all computed groups
plugin.getGroups(); // ColumnGroup[]
// Get columns in a specific group
plugin.getGroupColumns('personal'); // ColumnConfig[]
// Force refresh of column groups
plugin.refresh();

When using GroupingColumnsPlugin together with the Reorder Columns plugin, column reordering does not enforce group boundaries by default. Users can drag columns out of their groups, which will cause the groups to be recomputed based on the new column order.

Set lockGroupOrder: true to automatically prevent columns from being moved outside their group:

grid.gridConfig = {
features: {
groupingColumns: { lockGroupOrder: true },
},
};

This blocks moves that would break group contiguity in both header drag-and-drop and the visibility panel.

For more control, the column-move event is cancelable, so you can implement custom validation:

grid.on('column-move', ({ field, fromIndex, toIndex, columnOrder }, e) => {
// Example: prevent moves that break group boundaries
if (!isValidMoveWithinGroup(field, fromIndex, toIndex)) {
e.preventDefault(); // Column snaps back to original position
return;
}
// Persist the new order
saveColumnOrder(columnOrder);
});

The column-move event includes the complete columnOrder array, making it easy to persist user preferences.

The column grouping plugin supports CSS custom properties for theming. Override these on tbw-grid or a parent container:

PropertyDefaultDescription
--tbw-grouping-columns-header-bgvar(--tbw-color-header-bg)Group header row background
--tbw-grouping-columns-bordervar(--tbw-color-border)Group borders
--tbw-grouping-columns-separatorvar(--tbw-color-border-strong)Separator between groups
--tbw-button-padding-sm0.25rem 0.5remGroup header cell padding
--tbw-font-size-sm0.9285emGroup header font size
tbw-grid {
/* Custom column grouping styling */
--tbw-grouping-columns-header-bg: #e3f2fd;
--tbw-grouping-columns-border: #90caf9;
--tbw-grouping-columns-separator: #1976d2;
}

The column grouping plugin uses these class names:

ClassElement
.header-group-rowGroup header row container
.header-group-row.no-bordersBorderless mode
.header-group-cellIndividual group header cell
.header-row .cell.groupedColumn under a group
.header-row .cell.group-endLast column in a group
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