Row Spanning
Introduction
Section titled “Introduction”Row spanning merges consecutive cells in a column that share the same value into a single tall cell — the vertical equivalent of a merged header. It turns a run of repeated values (a region, a category, an owner) into clean visual groups without changing your data. The cells below the anchor are simply not drawn, so there are no internal gridlines inside a group.
How it works
Section titled “How it works”Row spanning ships in @zengrid/enterprise. Turn it on by passing the
rowSpanning() provider to the grid’s spanProvider option, then give each column
you want to merge a rowSpan resolver. For each anchor row the grid asks the
resolver how many following rows the cell should span; those rows are absorbed into
the tall anchor cell. Spanning is computed only for the rows in the render
window, so it stays fast at a million rows — the resolver is never called once
per hidden row.
Because a span merges a run of equal, consecutive values, sort your data by the spanned column first so the matching values sit next to each other.
interface ColumnDef { field: string; header: string; width?: number; /** * Return how many rows this cell should span (1 = no span). Called once per * anchor row, only for rows in the render window. */ rowSpan?: (params: RowSpanParams) => number;}
interface RowSpanParams { /** Value of the anchor cell — this column at `rowIndex`. */ value: unknown; /** Absolute row index of the anchor cell. */ rowIndex: number; /** Read this column's value at any absolute row, to look ahead. */ getValue: (row: number) => unknown;}import { rowSpanning } from '@zengrid/enterprise/grid';
const grid = new Zengrid(el, { rowCount, // Enable row spanning (Enterprise). Reads each column's rowSpan resolver. spanProvider: rowSpanning(), columns: [ { field: 'region', header: 'Region', width: 160, // Walk forward while the next row shares this value. rowSpan: ({ value, rowIndex, getValue }) => { let span = 1; while (getValue(rowIndex + span) === value) span++; return span; }, }, { field: 'name', header: 'Name', width: 240 }, { field: 'revenue', header: 'Revenue', width: 160 }, ],});Try it live
Section titled “Try it live”Each tab spans a different way. Edit the rowSpan resolver — return a bigger or
smaller count — and the grid re-merges. Row spanning follows display order, so
these demos build their rows already grouped (columns line up with the data
positionally: the first column is the first value in each row).
The Region column merges each run of equal values into one tall cell. Widen a group by returning a larger span, or delete rowSpan to see the ungrouped column.
- Sort first. A span only merges a consecutive run of equal values. Group your data (sort by the spanned column) before rendering, or spans of 1 are all you’ll get.
- Window-local by design. When a group starts above the visible area, the top visible row becomes the anchor for the rest of the run. This is what keeps spanning O(visible rows) at any scroll offset.
- Other columns are untouched. Only columns with a
rowSpanresolver merge; every other column still renders one cell per row.
- Row Data — supply the rows to group.
- Row Sorting — order the data so runs form.