Capítulo 31 de 54

Chapter 31: Column Pinning (React) Guide

Core Idea

columnPinningFeature splits columns into logical start/center/end regions (start≈left, end≈right in LTR — swapped in RTL), which you render either as one table using sticky CSS positioning, or as genuinely separate tables per region using the region-scoped getters.

Key Concepts

  • State shape: columnPinning: { start: string[], end: string[] } — everything not listed is implicitly "center" (unpinned). Pin defaults via initialState.columnPinning: { start: [...], end: [...] }.
  • Column order interaction: pinning is the first stage of the three-stage order pipeline (pin → manual columnOrder → grouping) — columnOrder only reorders within the center region; the only way to reorder within a pinned side is editing columnPinning.start/.end directly.
  • Ownership: external atom via atoms: { columnPinning: atom } (v9-recommended) or classic state.columnPinning + onColumnPinningChange — same tradeoffs as any other state slice.
  • Column-level APIs: column.getCanPin(), column.pin('start' | 'end' | false) (pin/unpin), column.getIsPinned() (which side, or false), column.getPinnedIndex() (position within its pinned group), column.getStart()/column.getAfter() (the correct left/right-equivalent CSS offset for sticky positioning), column.getIsFirstColumn()/getIsLastColumn() (edge-of-group check, useful for a boundary box-shadow).
  • Table-level APIs: table.setColumnPinning({...}), table.resetColumnPinning() (→ initialState), table.resetColumnPinning(true) (→ both arrays empty); table.getIsSomeColumnsPinned() (optionally scoped to 'start'/'end').
  • Region-scoped getters exist for basically everything: leaf columns (getStartLeafColumns/getCenterLeafColumns/getEndLeafColumns, plus *VisibleLeafColumns variants), header/footer groups (getStartHeaderGroups/getCenterHeaderGroups/getEndHeaderGroups, get*FooterGroups), flat/leaf headers per region, and row-level row.getStartVisibleCells()/getCenterVisibleCells()/getEndVisibleCells(). table.getPinnedLeafColumns(position) / getPinnedVisibleLeafColumns(position) take the region as a parameter instead.
  • Two rendering strategies: (1) single table + sticky CSS — render normally with table.getHeaderGroups()/row.getVisibleCells(), use column.getIsPinned()/getStart()/getAfter() to apply position: sticky and offsets; (2) split tables — render 2-3 separate <table>s using the region-scoped getters, one per pinned side plus center, when you want fully independent scroll/layout per region.

Code Examples

const features = tableFeatures({ columnPinningFeature })
const table = useTable({
  features, columns, data,
  initialState: { columnPinning: { start: ['expand'], end: ['actions'] } },
})

// sticky-CSS single-table approach
const style = column.getIsPinned() === 'start'
  ? { position: 'sticky', left: column.getStart(), zIndex: 1 }
  : column.getIsPinned() === 'end'
    ? { position: 'sticky', right: column.getAfter(), zIndex: 1 }
    : undefined
  • What it demonstrates: the sticky-CSS approach needs no separate tables — getIsPinned() plus getStart()/getAfter() is enough to position any pinned column correctly relative to its neighbors.

Key Takeaways

  1. Pick one rendering strategy (sticky CSS vs. split tables) per table — mixing them is unnecessary complexity.
  2. Reordering within a pinned side means editing columnPinning.start/.end directly — setColumnOrder doesn't reach pinned columns.
  3. start/end are logical, not literal left/right — they flip automatically under RTL, so don't hardcode left/right styling logic based on the pin side name alone.

Connects To

  • Column Ordering (React) Guide: how pinning and manual ordering combine in the column-order pipeline.
  • Row Pinning (React) Guide: the row-level equivalent of this same start/end split.
  • Cell Selection (React) Guide: how pinned regions interact with selection range boundaries.