Capítulo 41 de 54
rowExpandingFeature covers two distinct use cases that share the same state/toggle mechanism: real hierarchical sub-rows (via getSubRows) and custom "detail panel" UI unrelated to the table's own columns (via getRowCanExpand overriding the default sub-rows check) — picking the wrong one is the main source of confusion.
rowExpandingFeature + expandedRowModel: createExpandedRowModel() for client-side; manualExpanding: true for server-side (you own the expansion in your own data model, row model skipped).{ ..., children: Person[] }), point getSubRows: (row) => row.children at it — the table then understands where to find children, and row.getCanExpand() returns true automatically wherever subRows are found. Beware: a complex getSubRows runs for every row and sub-row, so keep it cheap; async functions aren't supported.getRowCanExpand: (row) => true (or your own predicate) instead of relying on subRows detection, then render a second <tr> conditionally on row.getIsExpanded() with a single <td colSpan={row.getAllCells().length}> holding custom content.ExpandedState = true | Record<string, boolean> — true means every row expanded; a record expands only the ids present with value true (a false entry, or a missing id, means collapsed).atoms: { expanded: atom } (v9-recommended) or classic state.expanded + onExpandedChange.cell renderer: row.getCanExpand() && <button onClick={row.getToggleExpandedHandler()}>{row.getIsExpanded() ? '▼' : '▶'}</button>.row.getCanExpand(), row.getIsExpanded(), row.getIsAllParentsExpanded() (useful to skip rendering a deeply nested row whose ancestor chain isn't fully open), row.toggleExpanded(), row.getToggleExpandedHandler().table.getCanSomeRowsExpand(), getIsAllRowsExpanded(), getIsSomeRowsExpanded(), getExpandedDepth(), toggleAllRowsExpanded()/getToggleAllRowsExpandedHandler(), table.setExpanded(...), resetExpanded() (→ initialState.expanded) / resetExpanded(true) (→ fully collapsed).filterFromLeafRows/maxLeafRowFilterDepth, same options as the Column Filtering Guide — needed here because expansion introduces the parent/child relationship that leaf-up filtering walks); pagination (paginateExpandedRows: false keeps a parent's children always on the parent's page, at the cost of rendering more rows than the nominal page size — default is true, so expanded rows paginate along with everything else and a subtree can span pages); row pinning (works the same as regular row pinning); sorting (expanded rows sort along with the rest by default).expanded state auto-resets whenever the grouped row model recomputes (e.g. on data or grouping-state change) — disabled automatically once manualExpanding: true, or override explicitly with autoResetExpanded: false (or the global autoResetAll: false). Set autoResetExpanded: false deliberately whenever inline data editing would otherwise collapse the user's open rows on every keystroke — pair it with autoResetPageIndex: false if pagination is also in play.// hierarchical sub-rows
const table = useTable({ features, columns, data, getSubRows: (row) => row.children })
// detail-panel UI, unrelated to real sub-rows
const table = useTable({ features, columns, data, getRowCanExpand: () => true })
<>
<tr>{row.getVisibleCells().map((cell) => <td key={cell.id}><table.FlexRender cell={cell} /></td>)}</tr>
{row.getIsExpanded() && <tr><td colSpan={row.getAllCells().length}>{/* custom detail UI */}</td></tr>}
</>
getSubRows for real hierarchy vs. getRowCanExpand + a manually-rendered second <tr> for unrelated detail content.getSubRows) vs. detail panel (getRowCanExpand override) — they use different setup even though the toggle APIs are identical.autoResetExpanded: false is essential whenever the table supports inline editing; otherwise every edit silently collapses open rows.paginateExpandedRows: false is the fix when users expect a parent's children to always render together with it, at the cost of variable page sizes.subRows, depth, parentId — the underlying row-tree properties this feature toggles visibility of.filterFromLeafRows/maxLeafRowFilterDepth, needed once filtering and expansion combine.