Multiple Columns
Introduction
Section titled “Introduction”Group Display Types introduced the
three ways to surface a group hierarchy. This page is a deep dive on
displayType: 'multipleColumns' — instead of collapsing every level into one
indented column (Single Column), each
groupBy level gets its own leading column. A row’s group header sits in the
column that matches its nesting level; leaf rows stay blank in the group columns
and render as ordinary data rows. Aggregates render in their own data column,
formatted by that column’s renderer.
Ask the manager for those columns with getGroupColumns() — it returns one
column per grouping level — and spread them ahead of your data columns:
const grouping = rowGrouping({ groupBy: [2, 5], // Region, then Status — a column each displayType: 'multipleColumns', aggregations: { 3: 'sum' }, // renders in the real Revenue column});
new Zengrid(mount, { columns: [...grouping.getGroupColumns(), ...dataColumns], fullWidthProvider: grouping.provider,});Because the manager reshapes the rows to match the prepended columns, keep your data columns contiguous from the first source index — the group columns own the leading slots.
Customising each group column
Section titled “Customising each group column”With one column per level, the tuning is per column. All of these are options
on rowGrouping():
groupColumnLabel(index, spec)— the header text for each group column.indexis the level (outermost is0) andspeccarries thecolit groups by, so you can name each column after its source column.groupColumnWidth— a single number sizes every group column the same, or pass a function(index, spec) => numberto size each level’s column independently (e.g. a wideRegioncolumn and a narrowStatusone).suppressCount— hide the(N)leaf-count suffix after each label.groupCellRenderer(params)— draw the group cell yourself. It’s called for every group column, so branch onparams.levelto render each level differently. Return an HTML string or anHTMLElement; the chevron and click-to-toggle stay wired.
const labels = { 2: 'Region', 5: 'Status' };
rowGrouping({ groupBy: [2, 5], displayType: 'multipleColumns', groupColumnLabel: (_index, spec) => labels[spec.col], groupColumnWidth: (_index, spec) => (spec.col === 2 ? 220 : 140), groupCellRenderer: ({ label, level }) => `${level === 0 ? '🌐' : '•'} <strong>${label}</strong>`,});Try it live
Section titled “Try it live”The default multiple-columns layout: Region and Status each get their own leading column (Group 1, Group 2). The Revenue aggregate renders in the real Revenue column. Collapse a Region and watch its Status column blank out.
- One column per level.
getGroupColumns()returns exactly as many columns as you havegroupBylevels. Each header lands in the column matching its nesting depth; deeper columns blank out for shallower rows. For the one-column, indent-by-depth layout use Single Column. - Per-column, not per-level indent.
indentPerLevelis a single-column concern and is ignored here — the hierarchy is expressed by which column holds the header, not by indentation. - Aggregates render in their column. With no full-width header to host them,
each configured aggregate lands in its real data column, formatted by that
column’s own
renderer(here,currency). groupCellRendererruns for every group column. Branch onparams.levelto style each level differently; it only replaces the label + count, so the chevron and click-to-toggle stay wired around your content.- Keep data columns contiguous. With positional array rows the group columns take the leading slots; your data columns must follow in source order so each cell lands under the right column.
- Group Rows — the full-width header layout.
- Grouping Toolbar — drag columns to group interactively.