Home/Columns/Column Groups
Columns

Column Groups

Span a header across several columns, and nest those spans as deep as your data does. Add columnGroupShow to a child and the group becomes collapsible — a compact summary when closed, the detail columns when open.

Nesting — a header row per level

Three levels: Employee over Identity and Contact, Employment over Compensation. The grid renders one header row per level of nesting and works out every span for you. Scroll sideways and watch a group's label stay with it.

Employee
Employment
Identity
Contact
Compensation
#
First name
Surname
Email
Phone
Department
Role
Salary
Bonus
1
Nadia
Haddad
nadia.haddad@example.com
+20 10 1000 2000
Engineering
Staff Engineer
68000
2000
2
Ben
Okafor
ben.okafor@example.com
+20 10 1037 2053
Design
Product Designer
105000
7300
3
Yara
Nasser
yara.nasser@example.com
+20 10 1074 2106
Data
Data Engineer
142000
12600
4
Karim
Farouk
karim.farouk@example.com
+20 10 1111 2159
Support
Support Specialist
83000
3900
5
Elena
Novak
elena.novak@example.com
+20 10 1148 2212
Security
Security Analyst
120000
9200
6
Walid
Basha
walid.basha@example.com
+20 10 1185 2265
Finance
Finance Controller
157000
14500
7
Alice
Smith
alice.smith@example.com
+20 10 1222 2318
People
Recruiter
98000
5800
8
Petra
Lange
petra.lange@example.com
+20 10 1259 2371
Product
Technical Writer
135000
11100
9
Idris
Salem
idris.salem@example.com
+20 10 1296 2424
Engineering
Staff Engineer
76000
2400
10
Tara
Aziz
tara.aziz@example.com
+20 10 1333 2477
Design
Product Designer
113000
7700
1–10 of 24

A group is any object with a children array, and [columns] takes a mix of those and ordinary leaf columns. A child may itself be a group, which is all nesting is:

{ name: 'Employee', children: [
  { name: 'Identity', children: [ …First name, Surname… ] },
  { name: 'Contact',  children: [ …Email, Phone… ] },
] }

One header row per level. This grid is three deep, so the header is three rows tall — two rows of spanners and the leaf row. Every span is counted for you from the shape of the array; there is no colspan to keep in sync. The # column is in no group and still occupies its own cell in each of the rows above it.

A group label stays with its group while you scroll. Scroll the body sideways and Employment stays pinned at the leading edge for as long as any of its columns is still on screen, then leaves with the last of them. That is stickyLabel, on by default; set it to false on a group and the label scrolls off with its first column.

Drag a group's trailing edge and every column inside it resizes at once. The delta is shared out proportionally, and each child still stops at its own minWidth/maxWidth. Individual columns keep their own handles. Set resizable: false on the group to lock the group-level edge alone.

Collapsible — columnGroupShow

Employment starts open and Contact starts closed. Press either chevron: the 'open' children appear, the 'closed' summary swaps out, and a width you dragged first is still there when it comes back.

Employment
Contact
#
Department
Salary
Bonus
Start date
Phone
1
Engineering
68000
2000
2014-01-10
+20 10 1000 2000
2
Design
105000
7300
2015-02-11
+20 10 1037 2053
3
Data
142000
12600
2016-03-12
+20 10 1074 2106
4
Support
83000
3900
2017-04-13
+20 10 1111 2159
5
Security
120000
9200
2018-05-14
+20 10 1148 2212
6
Finance
157000
14500
2019-06-15
+20 10 1185 2265
7
People
98000
5800
2020-07-16
+20 10 1222 2318
8
Product
135000
11100
2021-08-17
+20 10 1259 2371
9
Engineering
76000
2400
2022-09-18
+20 10 1296 2424
10
Design
113000
7700
2023-01-19
+20 10 1333 2477
1–10 of 24

A group becomes collapsible the moment one of its children carries a columnGroupShow — a chevron appears on the spanner and the group starts answering to it. There are three values, and the third is the default:

  • 'open' — on screen only while the group is expanded. The detail: Salary, Bonus and Start date here.
  • 'closed' — on screen only while the group is collapsed. The summary that stands in for the detail: Status here.
  • 'everywhere', or leaving it out — always on screen. Department here, which is why the group never vanishes entirely.

A collapsible group starts open. openByDefault defaults to true, so Employment needs no line at all; Contact carries openByDefault: false, and that is the entire difference between the two on this page. Contact shows only Phone until you open it, because Email is its one 'open' child.

A collapsible group needs at least one column that survives each state — a 'closed' child or one with no columnGroupShow at all. A group whose children are all 'open' would have nothing left once it closed, and its spanner would go with them: no chevron, and nothing left to click to bring it back. The mirror case is a group whose children are all 'closed', which would be missing before you touched anything.

So the grid does not offer the toggle at all in either case: the group renders as a plain spanner with every child visible, and a development-mode console message names the group and what to add. A toggle you must not press is not a toggle. That is also why Contact's Phone column carries no columnGroupShow — it is the child that makes Contact genuinely collapsible.

Widths survive a collapse. Drag Salary wider, collapse Employment, expand it again: the width you set is still there. Hidden children are hidden, not rebuilt, so nothing the reader has adjusted is thrown away.

Sorting, filtering and resizing behave on a grouped leaf exactly as they do on an ungrouped one. Grouping is a header arrangement, and it changes nothing below the header.

What a group is

[columns] accepts ColumnOrGroup<T>[] — leaf columns, groups, or both in any order. A group is an object with a children array and a name for its spanner label; a child can be another group, so the nesting goes as deep as your data does. Every header row and every span is derived from that one array.

Nothing about a group reaches the rows. Cells, sorting, filtering, editing and export all see the leaf columns exactly as they would if the groups were not there — which is why grouping can be added to an existing grid without touching anything else.

Collapsible groups

columnGroupShow on a child is what makes its parent collapsible. Give at least one child 'open' or 'closed' and a chevron appears on the spanner; leave every child without it and the group is a static header.

  • 'open' — shown while the group is expanded.
  • 'closed' — shown while the group is collapsed. The usual use is one compact summary column standing in for several detail ones.
  • 'everywhere' or unset — always shown.

openByDefault is true unless you say otherwise, so a collapsible group starts expanded and openByDefault: false is the line that starts one closed. A nested group can carry its own columnGroupShow as well, and then it appears or disappears with its parent's state.

Always leave a collapsible group something to show in each state — a 'closed' child, or one with no columnGroupShow at all. A group's spanner is drawn over its visible columns, so a group whose children are all 'open' would lose its header along with them the moment it collapsed. Rather than let that happen, the grid declines the toggle: no chevron, every child visible, and a dev-mode console message naming the group. The same applies to all-'closed' children, which would be missing while the group is open.

Resizing a group

With config.columnResize on, a group's trailing header edge is a resize handle for every visible column inside it: the delta is distributed proportionally and each child is clamped to its own minWidth/maxWidth. A child with resizable: false keeps its width and is left out of the distribution. Set resizable: false on the group to lock only the group-level edge — the children keep their own handles.

Keeping a group together

marryChildren: true asks the grid to keep a group's columns adjacent while the reader drags one of them, so a reorder cannot leave a hole in the middle of a group. It is best-effort rather than a hard lock.

The label while scrolling

A group wider than the viewport would otherwise lose its label off the leading edge, so by default the label is pinned there (after any pinned columns) for as long as one of the group's columns is visible. stickyLabel: false turns that off per group.

columnGroupShow: 'closed' is the half people forget. A group that only hides columns reads as a disappearing act; a group that swaps three detail columns for one summary column reads as a level of zoom.

Column group options

PropertyTypeDefaultDescription
group.childrenColumnOrGroup<T>[]—Any object with children renders as a spanning header. A child may be another group, so nesting has no ceiling — one header row is rendered per level.
group.name / headerNamestring—The spanner label. headerName is an alias; name wins if both are set.
group.openByDefaultbooleantrueWhether a collapsible group starts expanded. Set false to start it closed.
column.columnGroupShow'open' | 'closed' | 'everywhere''everywhere'Declared on a CHILD, and what makes the parent collapsible. 'open' shows while expanded, 'closed' while collapsed, unset always.
group.columnGroupShow'open' | 'closed' | 'everywhere''everywhere'The same option on a nested group — it appears or disappears with its parent group's state.
group.resizablebooleantrueDrag the group edge to resize every visible child at once, proportionally and clamped per child (needs config.columnResize). Set false to lock only the group-level edge.
group.stickyLabelbooleantrueKeep the label pinned at the leading edge while the group is scrolled horizontally, until its last column leaves. Set false to let it scroll away with the first column.
group.marryChildrenbooleanfalseBest-effort: keeps the group's columns adjacent while the reader drag-reorders one of them.
group.headerClassstring—CSS class on the group spanner cell — for colouring or spacing a band of related columns.
group.headerTooltipstring | () => string | nullthe group nameText for the spanner's tooltip. A group header already shows its own name unconditionally; set this only to say something different. See Tooltips.
group.headerGroupTemplateTemplateRef<HeaderGroupContext>—Custom spanner content. The context carries name, open and collapsible. See Templates.