Capítulo 9 de 54

Chapter 9: Data Guide

Core Idea

Everything else in TanStack Table's type system flows from your data array's type (TData) — define it once, correctly, and columns/rows/cells get inferred types for free; get the shape wrong and every downstream API loses its inference.

Key Concepts

  • data is an array of row objects; its element type becomes the TData generic threaded through table/column/row/cell types everywhere. Column definitions must use the same TData as the data passed to useTable.
  • data needs a stable reference. A new array identity every render (data={rawApiResponse.map(...)} inline) defeats memoization and forces the whole table to recompute — keep it in module scope, useState, useMemo, or a query cache (e.g. TanStack Query), never recreated inline on every render.
  • Deep/nested data: dot-path an accessorKey (accessorKey: 'name.first') to reach nested fields, or use accessorFn: (row) => row.info.age for anything an accessor path can't express. A literal period inside a key name breaks accessorKey path parsing — use accessorFn for those keys instead.
  • Sub-row / tree data: an optional subRows?: Array<TData> field (any name, configurable) on each row object is what the Expanding feature walks to build nested rows — no separate tree data structure needed.
  • Unknown-shape data (arbitrary API responses, uploaded CSVs, user-configured reports): type rows as a generic record (e.g. Record<string, unknown>) rather than forcing a nominal type, and build columns dynamically from the data's actual keys at runtime.

Code Examples

type User = { firstName: string; lastName: string; subRows?: User[] }

// stable reference — module scope, state, or a query result, never inline
const data: User[] = []

const columns: ColumnDef<typeof features, User>[] = [
  { header: 'First', accessorKey: 'firstName' },
  { header: 'Nested', accessorKey: 'address.city' },      // dot-path
  { header: 'Computed', accessorFn: (row) => `${row.firstName} ${row.lastName}`, id: 'fullName' },
]
  • What it demonstrates: three ways to point a column at data — direct key, dot-path into nested data, and a function for anything else — all against one User type that also declares optional subRows for expansion.

Key Takeaways

  1. Get TData right first — every column/row/cell type error traces back to a mismatched or unstable data type.
  2. Never construct the data array inline in the render body; that alone causes most "table re-renders/recomputes constantly" bugs.
  3. Reach for accessorFn whenever an accessorKey dot-path can't express the access (computed values, keys with literal periods, deeply conditional lookups).
  4. For expandable trees, just add subRows to your data type — no separate tree-building step.

Connects To

  • Column Definitions Guide: the full accessorKey/accessorFn/id contract this chapter previews.
  • Expanding (React) Guide: how subRows becomes expandable UI.
  • Client-Side vs Server-Side Guide: what changes when data arrives paginated/sorted/filtered from a server instead of as one full array.