Capítulo 26 de 54

Chapter 26: FlexRender (React) Guide

Core Idea

FlexRender (component, capital F) is the correct default for rendering a header/cell/footer; the lower-level flexRender() function exists for when you already have a resolved renderer value and its context and don't need FlexRender's table-specific decisions (aggregated-cell fallback, placeholder suppression).

Key Concepts

  • FlexRender takes exactly one table objectcell, header, or footer — and resolves the right column-def property and context itself: <table.FlexRender header={header} />, <table.FlexRender cell={cell} />. It's attached to the table instance, or importable directly as <FlexRender footer={header} /> for footer groups (footer groups are Header objects too, passed via the footer prop).
  • For cells specifically, FlexRender also decides which renderer to use: aggregatedCell when the cell is aggregated, falling back to cell if no aggregatedCell is defined, and rendering nothing for grouping placeholder cells. flexRender() alone does not make this decision — it just renders whatever definition/context pair you hand it.
  • flexRender(defOrNode, context) is the lower-level primitive: it distinguishes actual React components (including class components, memo, forwardRef) from already-created nodes and renders accordingly. Reach for it when you already have the specific definition and context resolved and don't need FlexRender's cell-vs-aggregatedCell or placeholder logic.
  • Renderer functions in column defs are treated as components and receive the appropriate context as props (header: ({ column }) => ..., cell: ({ getValue }) => ...) — don't call them as plain functions yourself.
  • cell.getValue()/cell.renderValue() are for when you only need the raw accessor value; use FlexRender (not those) whenever the column def might contain a static node or a component renderer, since FlexRender handles both and passes full context.
  • header.isPlaceholder is not automatically suppressed by FlexRender for headers — check it yourself in your render loop unless the placeholder intentionally carries content (e.g. a row-spanning header, see the Headers Guide's Header Row Spanning pattern).

Code Examples

// standard header/cell render loop — always through FlexRender
{table.getHeaderGroups().map((hg) => (
  <tr key={hg.id}>
    {hg.headers.map((header) => (
      <th key={header.id}>{header.isPlaceholder ? null : <table.FlexRender header={header} />}</th>
    ))}
  </tr>
))}
{table.getRowModel().rows.map((row) => (
  <tr key={row.id}>
    {row.getVisibleCells().map((cell) => <td key={cell.id}><table.FlexRender cell={cell} /></td>)}
  </tr>
))}

// low-level flexRender, when you already have the def+context resolved
flexRender(cell.column.columnDef.cell, cell.getContext())
  • What it demonstrates: the standard <thead>/<tbody> render loop, and the lower-level call FlexRender wraps internally.

Key Takeaways

  1. Default to table.FlexRender/<FlexRender ... />, not flexRender() directly, unless you specifically need the lower-level primitive.
  2. For cells, FlexRender already handles aggregated-vs-regular and grouping-placeholder logic — don't re-implement that branching yourself.
  3. header.isPlaceholder still needs an explicit check in your render loop; FlexRender doesn't suppress placeholders for you.

Connects To

  • Cells Guide / Headers Guide: the object types FlexRender renders.
  • Cell Spanning (React) Guide / Header Row Spanning (Headers Guide): related rendering conventions layered on top of FlexRender.