Column Groups
Group related columns under shared parent headers. OGrid supports arbitrarily nested column groups that render as multi-row <thead> headers.
Live Demo
The demo above uses Radix UI for styling. To see this feature with your framework's design system (Fluent UI, Material UI, Vuetify, PrimeNG, etc.), click "Open in online demo" below the demo.
Quick Example
- React
- Angular
- Vue
- Vanilla JS
import { OGrid } from '@alaarab/ogrid-react-radix';
import type { IColumnDef, IColumnGroupDef } from '@alaarab/ogrid-react-radix';
interface Person {
id: number;
name: string;
age: number;
email: string;
department: string;
salary: number;
status: string;
}
const columns: (IColumnDef<Person> | IColumnGroupDef<Person>)[] = [
{
headerName: 'Personal Info',
children: [
{ columnId: 'name', name: 'Name' },
{ columnId: 'age', name: 'Age', type: 'numeric' },
{ columnId: 'email', name: 'Email' },
],
},
{
headerName: 'Employment',
children: [
{ columnId: 'department', name: 'Department' },
{
columnId: 'salary',
name: 'Salary',
type: 'numeric',
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
{ columnId: 'status', name: 'Status' },
],
},
];
function App() {
return (
<OGrid
columns={columns}
data={people}
getRowId={(p) => p.id}
defaultPageSize={10}
/>
);
}
The OGrid component has the same props across all React UI packages. To switch, just change the import:
- Radix (lightweight, default):
from '@alaarab/ogrid-react-radix' - Fluent UI (Microsoft 365 / SPFx):
from '@alaarab/ogrid-react-fluent'— wrap in<FluentProvider> - Material UI (MUI v7):
from '@alaarab/ogrid-react-material'— wrap in<ThemeProvider>
import { Component } from '@angular/core';
import { OGridComponent } from '@alaarab/ogrid-angular-material';
@Component({
standalone: true,
imports: [OGridComponent],
template: `<ogrid [props]="gridProps" />`
})
export class GridComponent {
gridProps = {
columns: [
{
headerName: 'Personal Info',
children: [
{ columnId: 'name', name: 'Name' },
{ columnId: 'age', name: 'Age', type: 'numeric' },
{ columnId: 'email', name: 'Email' },
],
},
{
headerName: 'Employment',
children: [
{ columnId: 'department', name: 'Department' },
{
columnId: 'salary', name: 'Salary', type: 'numeric',
valueFormatter: (v: unknown) => `$${Number(v).toLocaleString()}`,
},
{ columnId: 'status', name: 'Status' },
],
},
],
data: people,
getRowId: (item: Person) => item.id,
defaultPageSize: 10,
};
}
Same component API across Angular packages. To switch, just change the import:
- Radix (CDK):
from '@alaarab/ogrid-angular-radix'(default, lightweight) - Angular Material:
from '@alaarab/ogrid-angular-material' - PrimeNG:
from '@alaarab/ogrid-angular-primeng'
All components are standalone — no NgModule required.
<script setup lang="ts">
import { OGrid } from '@alaarab/ogrid-vue-vuetify';
const columns = [
{
headerName: 'Personal Info',
children: [
{ columnId: 'name', name: 'Name' },
{ columnId: 'age', name: 'Age', type: 'numeric' },
{ columnId: 'email', name: 'Email' },
],
},
{
headerName: 'Employment',
children: [
{ columnId: 'department', name: 'Department' },
{
columnId: 'salary', name: 'Salary', type: 'numeric',
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
{ columnId: 'status', name: 'Status' },
],
},
];
const gridProps = {
columns,
data: people,
getRowId: (item) => item.id,
defaultPageSize: 10,
};
</script>
<template>
<OGrid :gridProps="gridProps" />
</template>
Same component API across Vue packages. To switch, just change the import:
- Radix (Headless UI):
from '@alaarab/ogrid-vue-radix'(default, lightweight) - Vuetify:
from '@alaarab/ogrid-vue-vuetify'— wrap in<v-app>for theming - PrimeVue:
from '@alaarab/ogrid-vue-primevue'
import { OGrid } from '@alaarab/ogrid-js';
import '@alaarab/ogrid-js/styles';
const grid = new OGrid(document.getElementById('grid'), {
columns: [
{
headerName: 'Personal Info',
children: [
{ columnId: 'name', name: 'Name' },
{ columnId: 'age', name: 'Age', type: 'numeric' },
{ columnId: 'email', name: 'Email' },
],
},
{
headerName: 'Employment',
children: [
{ columnId: 'department', name: 'Department' },
{
columnId: 'salary',
name: 'Salary',
type: 'numeric',
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
{ columnId: 'status', name: 'Status' },
],
},
],
data: people,
getRowId: (p) => p.id,
pageSize: 10,
});
This renders a two-row header: the first row shows "Personal Info" spanning three columns and "Employment" spanning three columns, with the individual column names in the second row.
How It Works
Column groups use the IColumnGroupDef<T> type. A group has a headerName (the text displayed in the group header cell) and a children array that can contain either IColumnDef items or nested IColumnGroupDef items.
// IColumnGroupDef<T>
{
headerName: string;
children: (IColumnGroupDef<T> | IColumnDef<T>)[];
}
When the columns prop contains groups, OGrid internally calls buildHeaderRows() to produce a multi-row header model. Each group header cell spans the correct number of leaf columns via colSpan.
Nested Groups
Groups can be nested to any depth. Each level adds another header row.
const columns: (IColumnDef<Row> | IColumnGroupDef<Row>)[] = [
{
headerName: 'Contact',
children: [
{
headerName: 'Name',
children: [
{ columnId: 'firstName', name: 'First' },
{ columnId: 'lastName', name: 'Last' },
],
},
{ columnId: 'email', name: 'Email' },
],
},
{ columnId: 'role', name: 'Role' },
];
This produces three header rows:
| Contact (span 3) | Role (span 1, rowSpan 3) |
|---|---|
| Name (span 2) | Email (rowSpan 2) |
| First | Last |
Mixing Groups and Standalone Columns
Standalone IColumnDef items at the top level are valid alongside groups. They span all header rows vertically.
Group Header Behavior
Group headers are display-only:
- Not sortable. Clicking a group header does nothing.
- Not filterable. No filter icon or popover appears on group headers.
- Not resizable. Group headers cannot be dragged to resize.
- Centered text. Group header labels are centered over their children by default.
Sorting, filtering, and resizing remain available on the leaf column headers beneath the groups.
Props Reference
| Type | Field | Description |
|---|---|---|
IColumnGroupDef<T> | headerName | Display text for the group header |
IColumnGroupDef<T> | children | Array of child columns or nested groups |
IOGridProps<T> | columns | Accepts (IColumnDef<T> | IColumnGroupDef<T>)[] |
Related
- Column Pinning -- pin grouped columns to the left edge
- Column Chooser -- show/hide columns within groups