Capítulo 8 de 54

Chapter 8: Features Guide

Core Idea

tableFeatures({...}) is v9's plugin architecture: every table statically declares which optional capabilities (sorting, filtering, pagination, ...) it needs, so unused feature code tree-shakes away and the TypeScript surface only exposes APIs for features that are actually registered.

Key Concepts

  • Why it's required: v8 was cautious about adding features because every new capability shipped to every user. v9's plugin architecture removes that constraint — potential bundle size rose (~14kB v8 → ~25kB v9 fully loaded), but most real apps ship less than v8 because they register only what they use.
  • Features, row models, and functions are three separate concerns: a feature (e.g. rowSortingFeature) adds state/options/APIs; a row-model factory (e.g. createSortedRowModel()) does the actual client-side data processing; a function registry (sortFns, filterFns) supplies named implementations you reference by string. You can register a feature for its state/APIs while doing the processing server-side — just omit the row-model factory and set the matching manual* option (see Client-Side vs Server-Side Guide).
  • tableFeatures() validates prerequisites at the type level: e.g. sortedRowModel requires rowSortingFeature, globalFilteringFeature requires columnFilteringFeature. A missing prerequisite is a type error naming what's missing, not a runtime surprise.
  • Define features outside the component (module scope) — it's static config; a stable reference avoids rebuilding table structures and gives shared column helpers one typeof features type to type against.
  • Register functions individually (filterFns: { includesString: filterFn_includesString }) to preserve tree-shaking; spreading a full built-in registry (e.g. all of filterFns) includes every implementation in it. A function passed inline to a column option doesn't need to be registered by name at all.
  • stockFeatures = every optional feature at once, for prototypes or "genuinely uses almost everything" tables — convenient but forfeits per-feature tree-shaking, and still doesn't add row models or function registries (add those separately).

Reference Tables

Stock featureAdds
cellSelectionFeaturespreadsheet-style cell-range selection state/APIs
cellSpanningFeaturecells spanning multiple rows/columns
columnFacetingFeatureunique values / ranges / counts per column, for filter UIs
columnFilteringFeatureper-column filter state and filtering
columnGroupingFeaturegrouping rows by column value
columnOrderingFeaturereordering columns
columnPinningFeaturepinning columns left/right
columnResizingFeatureresize interactions (builds on column sizing)
columnSizingFeaturecolumn width state
columnVisibilityFeatureshow/hide columns
globalFilteringFeaturecross-column search (builds on column filtering)
rowAggregationFeaturetotals/aggregates over row sets
rowExpandingFeatureexpand/collapse sub-rows
rowPaginationFeaturepage state + navigation (+ optional client-side pagination)
rowPinningFeaturepinning rows top/bottom
rowSelectionFeatureselected-row state/APIs
rowSortingFeaturesort state (+ optional client-side ordering)

Not in stockFeatures: fuzzy filtering (a recipe built from filtering + sorting) and virtualization (provided by TanStack Virtual, not a Table feature).

Code Examples

import {
  columnFilteringFeature, createFilteredRowModel, createSortedRowModel,
  filterFn_includesString, rowSortingFeature, sortFn_alphanumeric, tableFeatures,
} from '@tanstack/react-table'

const features = tableFeatures({
  columnFilteringFeature,
  rowSortingFeature,
  filteredRowModel: createFilteredRowModel(),  // client-side filtering
  sortedRowModel: createSortedRowModel(),       // client-side sorting
  filterFns: { includesString: filterFn_includesString },
  sortFns: { alphanumeric: sortFn_alphanumeric },
})
  • What it demonstrates: registering a feature's state/APIs (rowSortingFeature) is separate from opting into client-side processing for it (sortedRowModel: createSortedRowModel()) — server-driven tables register the feature but skip the row-model factory.

Key Takeaways

  1. Start from tableFeatures({}), add exactly the features a table needs — this is the mechanism the whole tree-shaking story depends on.
  2. If a table's data is sorted/filtered/paginated server-side, still register the feature for its state/APIs, but don't register the matching row-model factory.
  3. stockFeatures is a legitimate shortcut for prototypes or v8-parity, not a default to leave in production without reconsidering.

Connects To

  • Quick Start (React): first hands-on use of tableFeatures.
  • Client-Side vs Server-Side Guide: the manual* options that pair with omitting a row model.
  • Row Model Factories (API reference): every create*RowModel() function.
  • Custom Features (React) Guide: writing your own feature with the same plugin mechanism.