Row Pinning
Introduction
Section titled “Introduction”Row pinning holds chosen rows in a fixed band above or below the scrolling body, so they stay in view no matter where you scroll. Two things can be pinned:
- Existing dataset rows, pinned by a stable identity — the pin follows the row through sorting, filtering and pagination rather than sticking to a screen position.
- Standalone rows supplied by data — a header, a caption, or a computed
grand-total row that is not part of the dataset. This is the equivalent of AG
Grid’s
pinnedTopRowData/pinnedBottomRowData.
Both render in the same top/bottom bands, with the standalone rows sitting outermost: data rows above the pinned dataset rows on top, and below them on the bottom.
How it works
Section titled “How it works”Row pinning ships in @zengrid/enterprise. A RowPinManager is the headless
source of truth for what’s pinned; you hand it to the grid through the
pinProvider option via rowPinning():
import { RowPinManager, rowPinning } from '@zengrid/enterprise/grid';
const pins = new RowPinManager();pins.pin(0, 'top'); // pin a dataset row (by id) to the top bandpins.pin(42, 'bottom'); // …and one to the bottom
const grid = new Zengrid(el, { rowCount, pinProvider: rowPinning(pins), // Enterprise: turn on row pinning columns: [/* … */],});Pins are keyed by a stable row id, not a render index, so they survive sort, filter and pagination — the provider resolves each pinned id back to the row it currently occupies every render pass, and re-renders the bands automatically whenever you pin, unpin, or replace the standalone rows.
Standalone summary rows
Section titled “Standalone summary rows”To pin rows that aren’t in the dataset — a header or a computed total — give the
manager topRowData / bottomRowData, or set them later with setPinnedData.
Each is a plain object whose cells resolve by the column’s field:
const pins = new RowPinManager({ bottomRowData: [{ data: { name: 'Grand total', revenue: 1_240_000 } }],});// …later, recompute and swap the band in place:pins.setPinnedData('bottom', [{ data: { name: 'Grand total', revenue: next } }]);type RowId = string | number;type RowPinPosition = 'top' | 'bottom';
interface RowPinManagerConfig { /** Directions manual pinning may target. true = both. @default true */ enabled?: boolean | RowPinPosition; /** Guard consulted before a row is pinned — return false to forbid it. */ isRowPinnable?: (id: RowId) => boolean; /** Seed initial pins: called for each id in `rows`. */ isRowPinned?: (id: RowId) => RowPinPosition | null | undefined; rows?: readonly RowId[]; /** Standalone (non-dataset) rows pinned to each band. */ topRowData?: readonly PinnedDataRow[]; bottomRowData?: readonly PinnedDataRow[];}
interface PinnedDataRow { /** Row object; each cell reads `data[column.field]`. */ data: unknown; /** Explicit row height in px; falls back to the grid's default. */ height?: number;}Key manager methods: pin(id, position), unpin(id), toggle(id, position),
pinnedAt(id), getPinned(position), setPinnedData(position, rows),
clear(), plus onChange (dataset-row pins) and onDataChange (standalone
rows).
Try it live
Section titled “Try it live”Each tab pins a different way. Edit the snippet — change which rows are pinned, flip a band, or recompute a total — and the grid re-renders its bands.
pins.pin(id, band) fixes rows by id. Scroll the body — the first and last rows stay in their bands. Add pins.pin(2, "top") to pin another.
- Pins are identity-based.
pins.pin(3, 'top')pins the row whose id is3, not the fourth row on screen. When you sort or filter, the provider re-resolves each pinned id to its new position, so the pin follows the row. - Pinned rows also remain in the body. The bands are opaque overlays fixed to the top and bottom of the scroll area — a pinned row is drawn in its band and scrolls past underneath. This matches the familiar pinned-row behaviour.
- Standalone rows aren’t dataset rows. They ignore the enable mode and the
isRowPinnableguard, are unaffected by sort/filter, and resolve each cell from their own object by the column’sfield. Give one an explicitheightto make a taller summary row. - Bands re-render on change. Pinning, unpinning, or
setPinnedDataall notify the grid — no manual refresh needed.
- Row Data — supply the rows to pin.
- Row Sorting — pins follow the row through a sort.
- Row Spanning — merge repeated values into groups.