Capítulo 43 de 54

Chapter 43: Row Pinning (React) Guide

Core Idea

rowPinningFeature splits rows into top/center/bottom regions (mirroring column pinning's start/center/end), needs no row-model factory of its own, and by default keeps pinned rows visible even when filtering/pagination would otherwise remove them from view.

Key Concepts

  • Row reorder pipeline: only two features reorder rows, in this order — row pinning first (splits top/center/bottom), then sorting.
  • State shape: RowPinningState = { top: string[], bottom: string[] } (row ids). Default via initialState.rowPinning. Ownership: external atom (atoms: { rowPinning: atom }, v9-recommended) or classic state.rowPinning + onRowPinningChange.
  • Row-level API: row.getCanPin(), row.getIsPinned() ('top' | 'bottom' | false), row.getPinnedIndex(), row.pin('top' | 'bottom' | false). row.pin() also accepts includeLeafRows/includeParentRows flags — relevant when pinning a grouped/expanded row and deciding whether its related parent or leaf rows should move along with it.
  • Table-level API: table.getTopRows(), getCenterRows(), getBottomRows() — render these as three separate sections (or three separate <tbody>s) if pinned rows need visually distinct placement. table.getIsSomeRowsPinned() (optionally scoped to 'top'/'bottom'). table.setRowPinning({...}), table.resetRowPinning() / resetRowPinning(true) (clears both arrays).
  • Disabling: enableRowPinning on the table — boolean or a per-row predicate, e.g. (row) => row.original.status !== 'archived'.
  • keepPinnedRows (default true): a pinned row stays visible in its region even if it would otherwise be filtered or paginated out of the center rows. Set false if pinned rows should only render when actually present in the current filtered+paginated row model — i.e. pinning becomes purely cosmetic reordering, not an "always visible" guarantee.

Code Examples

const features = tableFeatures({ rowPinningFeature })
const table = useTable({ features, columns, data, initialState: { rowPinning: { top: ['0'], bottom: ['3'] } } })

<tbody>
  {table.getTopRows().map((row) => <PinnedRow key={row.id} row={row} />)}
  {table.getCenterRows().map((row) => <TableRow key={row.id} row={row} />)}
  {table.getBottomRows().map((row) => <PinnedRow key={row.id} row={row} />)}
</tbody>

// pin controls
row.getCanPin() && (
  <>
    <button onClick={() => row.pin('top')} disabled={row.getIsPinned() === 'top'}>Top</button>
    <button onClick={() => row.pin(false)} disabled={!row.getIsPinned()}>Center</button>
  </>
)
  • What it demonstrates: the three-region render loop and a minimal pin-control UI using getCanPin/getIsPinned/pin.

Key Takeaways

  1. keepPinnedRows: true (default) is what makes pinning behave like "always visible" rather than just reordering — decide deliberately if you want the opposite.
  2. Row pinning runs before sorting in the row-reorder pipeline — a pinned row's position within its own region is not affected by the table's sort state.
  3. Use includeLeafRows/includeParentRows on row.pin() when pinning interacts with grouped/expanded rows, to control whether relatives move together.

Connects To

  • Column Pinning (React) Guide: the column-level equivalent of this same start/center/end (here top/center/bottom) pattern.
  • Sorting (React) Guide: the feature that runs immediately after row pinning in the reorder pipeline.
  • Expanding (React) Guide: how pinning composes with expanded sub-rows.