Row pinning
Row pinning is available in Highcharts Grid Pro. It lets you keep selected rows visible at the top or bottom while the main rows scroll normally.
Pinned rows are existing data.dataTable rows. They remain in the scrollable
area and are also rendered in dedicated pinned sections.
Basic configuration
Grid.grid('container', {data: {dataTable: {columns: {id: ['row-001', 'row-002', 'row-999'],price: [1, 2, 999]}},idColumn: 'id'},rendering: {rows: {pinning: {topIds: ['row-001'],bottomIds: ['row-999']}}}});
In Grid Pro, row pinning is enabled by default. Use pinning.enabled: false to
disable row pinning UI:
Grid.grid('container', {rendering: {rows: {pinning: {enabled: false}}}});
When disabled, Grid still applies row pinning config (topIds, bottomIds,
resolve, etc.) and the runtime row pinning API continues to work. The
setting only disables built-in row pinning affordances such as context menu
actions.
const grid = Grid.grid('container', {data: {dataTable: {columns: {id: ['row-001', 'row-002', 'row-003'],priority: ['critical', 'normal', 'done']}},idColumn: 'id'},rendering: {rows: {pinning: {enabled: false,topIds: ['row-001'],resolve: (row) => row.priority === 'done' ? 'bottom' : null}}}});await grid.rowPinning.pin('row-002', 'top');
Use data.idColumn to define stable row identity for persistence and restore.
If it is not set, Grid uses a default row id.
Resolve pinning from row data
You can compute pinned rows dynamically with pinning.resolve.
The callback receives a row object and should return:
'top'to pin the row to the top section'bottom'to pin the row to the bottom sectionnullorundefinedto keep the row unpinned
Grid.grid('container', {data: {idColumn: 'id'},rendering: {rows: {pinning: {resolve: function (row) {if (row.priority === 'critical') {return 'top';}if (row.status === 'done') {return 'bottom';}return null;}}}}});
resolve is recomputed when query state changes. Explicit topIds and
bottomIds are applied first, and resolve adds additional IDs that are not
already pinned explicitly.
Runtime API
Use the scoped row pinning API to update pinning dynamically:
await grid.rowPinning.pin('row-001'); // defaults to topawait grid.rowPinning.pin('row-010', 'top');await grid.rowPinning.pin('row-025', 'bottom');await grid.rowPinning.pin('row-050', 'top', 0); // insert at index 0 in top sectionawait grid.rowPinning.toggle('row-025'); // defaults to top when currently unpinnedawait grid.rowPinning.toggle('row-030', 'bottom');await grid.rowPinning.unpin('row-010');const pinned = grid.rowPinning.getPinnedRows();// { topIds: [...], bottomIds: [...] }
In Grid Pro, you can also listen to runtime row pinning events in
rendering.rows.pinning.events:
Grid.grid('container', {rendering: {rows: {pinning: {events: {beforeRowPin: function (event) {console.log(this,'before',event.action,event.rowId,event.topIds,event.bottomIds);},afterRowPin: function (event) {console.log(this,'after',event.action,event.rowId,event.topIds,event.bottomIds);}}}}}});
Event payload fields:
rowId: changed row idaction:'pin' | 'unpin' | 'toggle'position: pin target when relevant ('top'for new toggle pins)index: insertion index when provided togrid.rowPinning.pin(...)changed: whether pinned state actually changedpreviousTopIds/previousBottomIdstopIds/bottomIds
beforeRowPin fires before redraw, and
afterRowPin fires after redraw.
Built-in context menu actions
Available built-in actions are:
pinRowToppinRowBottomunpinRow
For configuration and customization details, see Cell context menu.
Sorting and filtering behavior
Pinned sections always render from the original data source and are not affected by active sorting, filtering, or pagination.
Pinned rows also remain in the scrollable area, so they can appear in both sections at once.
For accessibility, Grid announces both representations and adds context so screen readers can distinguish rows rendered in pinned sections from rows in the main scrollable section that are also pinned.
Pinned section max height
You can make top/bottom pinned sections scroll independently by setting
pinning.top.maxHeight and/or pinning.bottom.maxHeight.
If maxHeight is not set, pinned sections do not use internal scrolling by
default.
When rows are pinned at runtime (grid.rowPinning.pin(...) or
grid.rowPinning.toggle(...) when it pins), Grid
automatically scrolls the affected top/bottom pinned section just enough to keep
the inserted row visible.
rendering: {rows: {pinning: {topIds: ['row-001', 'row-002', 'row-003'],bottomIds: ['row-998', 'row-999'],top: {maxHeight: 120 // pixels},bottom: {maxHeight: '25%' // percent of table height}}}}