Capítulo 18 de 54

Chapter 18: Headers Guide

Core Idea

Headers are the <thead> equivalent of cells, sourced from header groups (headerGroup.headers), and the one non-obvious piece is rowSpan/isPlaceholder handling for column trees where leaf columns sit at different depths.

Key Concepts

  • Get headers: iterate headerGroup.headers for a given row, or use table APIs like table.getFlatHeaders() for a flat list across all header rows; column-visibility/pinning add more variants (table.getStartLeafHeaders(), table.getEndFlatHeaders(), etc.).
  • header.id: for simple (non-grouped) tables, equals the parent column.id; for group columns or placeholder cells, it's a composite of header family/depth/column id/header-group id.
  • Nested/grouped-header properties: colSpan (columns spanned horizontally), rowSpan (rows spanned vertically for uneven column trees — see below), depth (which header row this belongs to), isPlaceholder (true for filler cells above a shallower leaf column's real header), subHeaders (child headers, empty for leaves).
  • header.index (position left-to-right within its header group) is not the same as header.depth (which header row/group it's in) — an easy naming trap.
  • Every header references its parent column and parent header group.
  • Rendering: always through flexRender(header.column.columnDef.header, header.getContext()) — the header column option can be a string, JSX, or a function, and flexRender normalizes all three.

Reference Tables

Header propertyMeaning
colSpanhow many leaf columns this header covers, horizontally
rowSpanhow many header rows this header covers, vertically (see Header Row Spanning below)
depthwhich header row (0-indexed) this header sits in
isPlaceholdertrue for filler cells above a shallow leaf's real header
subHeaderschild headers (empty array for leaf headers)

Worked Example

Header Row Spanning — when the column tree is uneven (some leaf columns nested deeper than others), each shallow leaf produces a chain of placeholder headers above its real header. The top placeholder in that chain reports the chain's full rowSpan; every header it covers (including the real leaf header below it) reports rowSpan: 0. To merge those visually into one tall <th>, skip any header with rowSpan === 0 and render the rest with the rowSpan attribute — this check replaces the usual isPlaceholder-based empty-cell rendering, for <thead> only:

{headerGroup.headers.map((header) =>
  header.rowSpan === 0 ? null : (
    <th key={header.id} colSpan={header.colSpan} rowSpan={header.rowSpan}>
      {flexRender(header.column.columnDef.header, header.getContext())}
    </th>
  )
)}

This convention is <thead>-only: footer groups render header rows in reverse order, which puts a spanning placeholder below the cells it would cover, so <tfoot> still uses the plain isPlaceholder → empty-cell pattern. The body-cell equivalent of row spanning is the separate cellSpanningFeature (Cell Spanning Guide).

Key Takeaways

  1. Render headers through flexRender, never by reading header.column.columnDef.header directly as if it were always a string.
  2. For uneven column trees, use the rowSpan === 0 skip pattern in <thead> — the ordinary isPlaceholder empty-cell pattern is for <tfoot> only (or flat, even trees).
  3. Most other header APIs relate to column sizing/resizing — see those guides rather than this one for header.getSize()-style APIs.

Connects To

  • Header Groups Guide: where headerGroup.headers comes from.
  • Cell Spanning (React) Guide: the body-cell equivalent of header row spanning.
  • Column Sizing / Column Resizing (React) Guides: the sizing-related header APIs not covered here.