Row Keys
computeRowKey returns the React key for a row — the value React uses to decide whether the row in this render is the same logical row as one from the previous render. Provide a stable key (typically derived from data.id), and React reconciles the row in place even when its index changes under a sort, filter, group, prepend, or append.
<DataTable computeRowKey={({ data }) => data.id} model={model}>
{/* columns */}
</DataTable>Why row keys matter
Section titled “Why row keys matter”Without computeRowKey, the key falls back to the virtual row position. That works for tables whose rows never move, but as soon as the model reorders — a pipeline filter stage runs, a sort stage reshuffles the result, a new page is appended, a remote refetch returns rows in a new order — every row whose index changed remounts.
The symptom is usually a user-visible glitch: a search input loses focus mid-keystroke because the row it lives in moved one position, an expanded detail row collapses because its parent shifted, a chart inside a cell redraws because the cell got a new React identity. The data is still correct; the React identity broke. computeRowKey is what stops that from happening.
This is a React reconciliation rule, not a table-specific concern. It applies to any data table where rows can move, regardless of which other table features you use.
Grouped data
Section titled “Grouped data”Grouped data mixes data rows and group rows in the same flat array (see Grouping). The function receives both shapes, so return a key for each:
<DataTable computeRowKey={({ data, index }) => ('id' in data ? data.id : `group:${data.label}:${index}`)} model={model}>
{/* columns */}
</DataTable>Group rows rarely carry a stable ID from your data, so a composite key built from a label plus index is fine — the table doesn’t try to reconcile group rows across regrouping anyway. The important thing is that data rows keep their identity through reorder; group rows are allowed to remount when the grouping dimension changes.
Function signature
Section titled “Function signature”computeRowKey receives an object with three fields:
data— the row at this index (the data row or the group row)index— the row’s virtual position in the tablecontext— the table’s ambient context, useful for scoping keys across tenants, saved views, or other instances that might share row IDs
<DataTable computeRowKey={({ data, context }) => `${context.tenantId}:${data.id}`} context={{ tenantId: 'acme' }} model={model}>
{/* columns */}
</DataTable>Cross-scope key prefixing matters when one component instance is reused for different tenants or views — without it, React would see the same key for what’s actually a different row and skip reconciliation it shouldn’t have skipped.