Capítulo 15 de 54

Chapter 15: Rows Guide

Core Idea

Row objects carry both the original data and every API needed to read state-aware values from that row — the two rules that trip people up most are: use row.getValue()/row.renderValue() instead of reaching into row.original for display, and use row.getDisplayIndex() instead of row.index for row numbers.

Key Concepts

  • Get a specific row: table.getRow(rowId). Get rendered rows: table.getRowModel().rows. Get only selected rows: table.getSelectedRowModel().rows.
  • row.id defaults to the row's index in the (core) row model, but override it with getRowId: (originalRow) => originalRow.uuid on useTable whenever you need a stable identifier that survives sorting/filtering/pagination (selection and expansion state key off this id). Grouping/expanding append extra characters to row.id for generated sub-rows.
  • row.indexrow.getDisplayIndex(). row.index is fixed at creation time (position in the parent array) and does not reflect filtering/sorting/grouping/expansion. For a visible row-number column, always call row.getDisplayIndex() (zero-based, -1 if the row isn't in the current display order) — never read the internal row._displayIndexCache directly, it can be stale.
  • Value access: row.getValue('colId') returns the accessor's value or undefined; row.renderValue('colId') returns the value or renderFallbackValue if undefined. Both cache the accessor result. cell.getValue()/cell.renderValue() are shortcuts bound to the cell's own column.
  • row.original is the untouched source object passed in data — accessor transformations never mutate it, so don't expect computed/derived accessor values to show up there.
  • Sub-rows (grouping/expanding): row.subRows, row.depth (0 = root), row.parentId, row.getParentRow(), and table.getMaxSubRowDepth() (memoized deepest structural depth).

Code Examples

const rowNumberColumn = columnHelper.display({
  id: 'rowNumber',
  header: '#',
  cell: ({ row }) => {
    const i = row.getDisplayIndex()
    return i === -1 ? '' : i + 1
  },
})

const uuidTable = useTable({
  features, columns, data,
  getRowId: (originalRow) => originalRow.uuid, // stable id across re-sorts/filters
})
  • What it demonstrates: a correct row-number column (via getDisplayIndex) and stable row identity (via getRowId) — the two most common row-related bugs when either is skipped.

Key Takeaways

  1. Never render row.index as a user-facing row number — use row.getDisplayIndex().
  2. Set getRowId whenever selection/expansion state needs to survive data refetches or reordering.
  3. Prefer row.getValue()/renderValue() over row.original.field for anything that goes through an accessor — it's cached and respects renderFallbackValue.

Connects To

  • Cells Guide: cell.getValue()/renderValue() as row-scoped shortcuts.
  • Expanding (React) Guide: subRows, depth, parentId in practice.
  • Row Selection (React) Guide: why getRowId matters for persisted selection.