Pinned and sticky rows
Application-owned rows can be pinned into top and bottom bands that sit outside the scrolling body, any row — including generated group rows — can be pinned at runtime, and expanded group ancestors can stick below the header while scrolling.
- React
- Angular
- Core TypeScript
const pinnedTop = [{ id: "target", label: "Target", amount: 1_000_000 }];
const pinnedBottom = [{ id: "total", label: "Total", amount: 842_000 }];
<Grid
rowIdKey="id"
rowData={rows}
columnDefs={columns}
rowNumbers
rowPinningMenu
pinnedTopRowData={pinnedTop}
pinnedBottomRowData={pinnedBottom}
/>
pinnedTop = [{ id: "target", label: "Target", amount: 1_000_000 }];
pinnedBottom = [{ id: "total", label: "Total", amount: 842_000 }];
<awb-grid
rowIdKey="id"
[rowData]="rows"
[columnDefs]="columns"
[rowNumbers]="true"
[rowPinningMenu]="true"
[pinnedTopRowData]="pinnedTop"
[pinnedBottomRowData]="pinnedBottom"
/>
const pinnedTop = [{ id: "target", label: "Target", amount: 1_000_000 }];
const pinnedBottom = [{ id: "total", label: "Total", amount: 842_000 }];
const core = new GridCore(measurer, {
rowIdKey: "id",
columnDefs,
rowNumbers: true,
rowPinningMenu: true,
pinnedTopRowData: pinnedTop,
pinnedBottomRowData: pinnedBottom,
});
api.setPinnedTopRowData(nextTargets); // replace a band at runtime
api.setRowPinned(groupNodeId, "bottom"); // pin a generated row
api.setRowPinned(groupNodeId, null);
Application-owned top and bottom rows
const options = {
pinnedTopRowData: [{ id: "target", label: "Target", amount: 1_000_000 }],
pinnedBottomRowData: [{ id: "total", label: "Total", amount: 842_000 }],
} satisfies GridOptions;
These rows sit outside sorting, filtering, grouping, pagination, and the row count. They retain normal formatting and cell renderers.
Replace a band at runtime through the API:
api.setPinnedTopRowData(nextTargets);
api.setPinnedBottomRowData(nextTotals);
Each application band caps at 30% of grid height and gets an independent vertical scrollbar when needed.
Editable application rows
const options = {
pinnedTopRowData: [{ id: "forecast", amount: 100 }],
pinnedRowsEditable: true,
columnDefs: [{ key: "amount", label: "Amount", editable: true }],
} satisfies GridOptions;
Edits update the supplied object and participate in undo/redo. Without
pinnedRowsEditable, pinned rows stay read-only regardless of column
editable flags, and synthetic group headers are never editable.
Pin generated rows
const options = {
isRowPinned: ({ node }) => node.isGroup && node.groupKey === "EMEA" ? "top" : null,
} satisfies GridOptions;
api.setRowPinned(groupNodeId, "bottom");
api.setRowPinned(groupNodeId, null);
Runtime-pinned hierarchy rows keep live expansion and aggregates. Their
ancestor chain is pinned with them, and unpinning the chain releases
descendants. Set rowPinningMenu: true to expose
Pin to top / Pin to bottom / Unpin in the body context menu — try it in the
demo above.
Sticky group ancestors
const options = {
groupRowsSticky: true,
groupDefaultExpanded: -1,
} satisfies GridOptions;
Expanded ancestors stack below the header while scrolling, in client-side and server-side grouping alike, and with all group display modes.
A docked header works like the group row it covers. A click selects its cell
and opens a click-trigger ActionFrame, and a double-click fires
rowDoubleClicked / cellDoubleClicked with the same node. Hovering it shows
the same tooltip, the context menu works there, and the chevron collapses the
group. These gestures leave the scroll position alone, because the docked
header is already on screen. Keyboard navigation onto a docked row still
scrolls to the row's own place in the body.
Selection, navigation, and export across bands
No extra option is required. Arrow navigation crosses top → body → bottom; range selection and Ctrl/Cmd+A span the unified sequence; copy and full export preserve that order. Cut and paste remain body-only.