TanStack
API Reference

Data Transforms

Transforms are ordinary functions: source rows in, typed derived data out. They do not rewrite mark options, retain state, cache results, or own framework reactivity.

Functions

ExportResult
foldWide rows repeated into authored field/value pairs
groupByNamed group fields, reducer outputs, and lineage
binX, binYNumeric intervals on one axis
binXYNumeric cells with x and y intervals
binTimeX, binTimeYCalendar-aligned intervals from a supplied time interval
windowFlat input rows extended with rolling outputs
cumulativeFlat input rows extended with running outputs
rankFlat input rows extended with ranks
normalizeFlat input rows extended with normalized values
selectSelected original rows
stackRowsX, stackRowsYFlat input rows extended with stack endpoints
mosaicX, mosaicYTwo normalized proportional interval dimensions
waterfallOrdered signed contributions as cumulative intervals
quantileA reusable quantile reducer factory
treeLayoutTidy-tree node and link rows in semantic coordinates
forceLayoutSettled nodes, resolved links, and padded x/y domains

Granular entry points are:

  • @tanstack/charts/transform
  • @tanstack/charts/transform/bin
  • @tanstack/charts/transform/bin-time
  • @tanstack/charts/transform/bin-xy
  • @tanstack/charts/transform/cumulative
  • @tanstack/charts/transform/fold
  • @tanstack/charts/transform/group
  • @tanstack/charts/transform/mosaic
  • @tanstack/charts/transform/normalize
  • @tanstack/charts/transform/rank
  • @tanstack/charts/transform/reduce
  • @tanstack/charts/transform/select
  • @tanstack/charts/transform/stack
  • @tanstack/charts/transform/waterfall
  • @tanstack/charts/transform/window
  • @tanstack/charts/hierarchy/tree
  • @tanstack/charts/network/force

Numeric, two-dimensional, and calendar bins are separate so specialized binning does not enlarge an ordinary histogram.

Numeric thresholds accepts a count, complete boundary array, or a D3-compatible threshold callback such as thresholdScott.

Fold wide rows

fold turns selected fields into long-form rows while preserving the other source fields:

ts
import { fold } from '@tanstack/charts/transform/fold'

const points = fold(rows, {
  fields: ['R90_10_1980', 'R90_10_2015'] as const,
  as: { key: 'periodField', value: 'inequality' },
})

Output is source-row-major, then follows authored fields order. Each point retains the source row's other properties and adds periodField, inequality, source, and sourceIndexes. Omitting as uses key and value. Values are not filtered, so null, undefined, and NaN remain available to subsequent transforms or application logic.

Use a literal field tuple so the output key and value remain correlated when TypeScript narrows the key. Duplicate fields, identical output names, and the reserved source or sourceIndexes output names throw synchronously with a fold: error. Output names may replace source fields; lineage retains the original row.

Group fields

by: 'region' preserves the field name in the result. Compound groups use a named object:

ts
const daily = groupBy(orders, {
  by: {
    region: 'region',
    day: ({ datum }) => utcDay.floor(datum.createdAt),
  },
  outputs: {
    revenue: { value: 'amount', reduce: 'sum' },
    orders: { reduce: 'count' },
  },
})

The result contains region and day, not an opaque key or tuple.

Reducers

Every output names its reducer. count omits value; numeric reducers require it. Compact built-in strings are count, sum, mean, min, and max. Tree-shakeable reducer functions provide median, variance, deviation, first, last, difference, and ratio without adding them to every aggregation bundle.

Custom reducers receive one object with values, selected data, source indexes, and the named group object. quantile(probability) returns a custom reducer.

Empty count and sum results are zero. Other empty numeric results are NaN. variance and deviation use the sample denominator and return NaN for fewer than two finite values. If a singleton group needs a zero-width interval, state that policy in a small authored reducer.

Numeric reducers ignore non-finite channel values, while source, sourceIndexes, and a custom reducer's data still describe every input row in the group. Filter invalid observations before groupBy when lineage should contain only contributors.

Ordering and flat rows

window and cumulative accept orderBy and ascending or descending order. Input order is used when orderBy is omitted. rank orders by its value and supports competition, dense, and ordinal ties.

One-to-one transforms spread the input row and add named outputs:

ts
const trends = window(daily, {
  by: 'region',
  orderBy: 'day',
  size: 28,
  partial: false,
  outputs: {
    revenue28d: { value: 'revenue', reduce: 'sum' },
    averageOrder28d: { value: 'averageOrder', reduce: 'mean' },
  },
})

lineY(trends, { x: 'day', y: 'revenue28d', color: 'region' })

There is no nested datum.datum path. A named output may intentionally replace an input field; the original rows remain available through lineage. Structural source and sourceIndexes names are reserved.

Mosaic intervals

mosaicY allocates outer category totals across x, then normalizes y values within each x category. Keep aggregation explicit so the definition shows whether a cell represents a count or a weighted sum:

ts
import { groupBy, mosaicY, rect } from '@tanstack/charts'

const counts = groupBy(responses, {
  by: { question: 'question', response: 'response' },
  outputs: { count: { reduce: 'count' } },
})

const cells = mosaicY(counts, {
  x: 'question',
  y: 'response',
  value: 'count',
  yOrder: ['Strongly disagree', 'Disagree', 'Neutral', 'Agree'],
})

rect(cells, {
  x: 'x',
  x1: 'x1',
  x2: 'x2',
  y: 'y',
  y1: 'y1',
  y2: 'y2',
  color: 'yValue',
})

Each row retains the aggregate input fields and adds semantic xValue and yValue, normalized centers and endpoints, the cell value, its outer-group total, the grand total, and direct lineage. mosaicX transposes the policy: y-category totals determine row heights and x values compose within each row. Use xOrder and yOrder to make categorical ordering explicit. Duplicate x/y pairs throw; aggregate them first with groupBy.

Waterfall intervals

waterfall turns signed contributions into ordered cumulative intervals. It does not derive the contributions themselves, so analytical intent remains visible beside the chart:

ts
import { barY, difference, window } from '@tanstack/charts'
import { waterfall } from '@tanstack/charts/transform/waterfall'

const changes = window(observations, {
  orderBy: 'year',
  size: 2,
  partial: false,
  outputs: { delta: { value: 'price', reduce: difference } },
})

const bridge = waterfall(changes, {
  value: 'delta',
  orderBy: 'year',
  total: true,
})

barY(bridge, {
  x: (row) => (row.kind === 'total' ? 'Total' : row.year),
  y1: 'start',
  y2: 'end',
  color: 'kind',
})

Each valid step retains the input row, adds delta, start, end, and an increase or decrease kind, and carries one-row direct lineage. Zero is an increase so it remains available to downstream policy. Nullish and nonfinite values are omitted. A nonfinite cumulative result throws instead of emitting invalid geometry.

total: true appends one zero-based total row for every nonempty group. The total is a discriminated synthetic row containing only group fields, derived fields, and aggregate direct lineage; it does not clone an arbitrary last source row. by, orderBy, and order use the same first-seen grouping and stable ordering contracts as the other eager transforms. Group output names cannot collide with waterfall or lineage fields.

Tidy hierarchy trees

treeLayout turns flat path or parent-reference rows into positioned nodes and links. Import it from its exact optional entry:

ts
import { treeLayout } from '@tanstack/charts/hierarchy/tree'

const hierarchy = treeLayout(rows, {
  path: 'name',
  delimiter: '.',
  orientation: 'left',
  nodeSize: [1, 1],
})

Use path and an optional one-character delimiter for full semantic paths. Missing path ancestors are imputed with data: null and empty lineage. Use id and parentId instead when every node is an explicit row:

ts
const hierarchy = treeLayout(rows, {
  id: 'id',
  parentId: 'parentId',
})

The two input forms are mutually exclusive. IDs must be unique, every non-root parent must exist, and the rows must form one acyclic hierarchy. sort and separation receive immutable TreeNodeContext objects with identity, raw data, depth, height, internal/external flags, and lineage. Input order remains the child order when sort is omitted.

Path-mode IDs use canonical slash form and name is the terminal path segment. Explicit-parent IDs are opaque, so name is the complete authored ID even when it contains a slash.

Output nodes contain id, parentId, name, nullable data, depth, height, internal, external, x, y, source, and sourceIndexes. Each link contains stable source and target IDs, resolved endpoint nodes and indexes, x1, y1, x2, y2, and the target node's raw-row lineage. A link uses its target ID as its own ID because each non-root tree node has one incoming link. Endpoint indexes are null when the corresponding path ancestor was imputed.

orientation selects the root anchor: left is the default, with right, top, and bottom also available. nodeSize is [breadth, depth] in semantic data-space units. Normal positional scales own responsive projection, so this eager transform does not depend on final chart bounds. It is uncached; memoize unchanged hierarchy input with other derived data.

Static force layouts

forceLayout runs a stopped D3 force simulation synchronously and returns ordinary rows for native marks. Import it from its exact optional entry:

ts
import { forceLayout } from '@tanstack/charts/network/force'

const graph = forceLayout(nodes, links, {
  nodeKey: 'id',
  source: 'source',
  target: 'target',
  iterations: 300,
  domainPadding: 0.2,
  forces: [
    { type: 'link', distance: 42 },
    { type: 'manyBody', strength: -120 },
    { type: 'center', x: 0, y: 0 },
    { type: 'collide', radius: 9, strength: 0.9 },
    { type: 'x', x: 0, strength: 0.03 },
    { type: 'y', y: 0, strength: 0.03 },
  ],
})

The force descriptors are explicit and applied in authored order. link accepts link-row distance and strength channels. manyBody.strength, collide.radius, and the x and y targets and strengths accept node-row channels. center coordinates and collide.strength are fixed values. Each force type may appear at most once.

The transform clones its inputs before D3 mutates simulation state. Output nodes retain non-reserved source fields and add x, y, vx, vy, source, and sourceIndexes. Output links retain their raw endpoint keys and add resolved node references, source and target indexes, x1, y1, x2, y2, sourceRows, and sourceIndexes. The result also contains xDomain and yDomain for configured positional scales.

Node keys must be unique, and every link endpoint must match one. Stable input order produces repeatable static settlement. The transform is eager, chart-size independent, and uncached; memoize it with other derived data when a framework component rebuilds unchanged input.

This API does not run a live simulation or own drag state. Products that need continuous physics or node dragging should keep that controller and its positions in application state, then render the current rows through normal marks.

Lineage

Aggregations expose source and sourceIndexes. Row-extending transforms expose the source rows used for that derived value. This supports inspection, tooltips, drill-down, and subsequent transforms without renderer knowledge.

Lineage is direct to the immediate input. In a foldnormalize pipeline, the normalized row points to its folded input, and that folded row points to the original source row. select returns the chosen input rows unchanged.

See Transforms and Reactivity for composition and memoization guidance.

Types

Value and grouping contracts are TransformAccessor, TransformAccessorContext, TransformField, TransformValue, TransformValueOutput, TransformKey, TransformGroupSpec, TransformGroupRow, TransformOrder, TransformOrderOptions, and TransformLineage.

Reducer contracts are TransformNumericReducer, TransformReducer, TransformReduceContext, TransformOutputSpec, TransformOutputs, TransformOutputValue, and TransformOutputRow.

Group exports are GroupByOptions and GroupByDatum. Numeric bin exports are BinOptions, BinXDatum, and BinYDatum. Two-dimensional bin exports are BinXYOptions and BinXYDatum. Calendar bin exports are TimeIntervalLike, BinTimeOptions, and BinTimeDatum.

Fold exports are FoldField, FoldOutputNames, FoldOptions, and FoldDatum.

Rolling exports are WindowOptions, WindowDatum, and WindowAnchor. Cumulative exports are CumulativeOptions and CumulativeDatum. Rank exports are RankOptions, RankDatum, and RankTies.

Normalization exports are NormalizeOptions, NormalizeDatum, NormalizeBasis, and NormalizeContext. Selection exports are SelectOptions, SelectMethod, and SelectContext. Row-stack exports are StackRowsXOptions, StackRowsXDatum, StackRowsYOptions, and StackRowsYDatum.

Mosaic exports are MosaicOptions, MosaicXDatum, and MosaicYDatum.

Waterfall exports are WaterfallKind, WaterfallOptions, WaterfallDatum, WaterfallStepDatum, and WaterfallTotalDatum.

Static force-layout exports are forceLayout, ForceLayoutOptions, ForceDescriptor, ForceNumericValue, ForceLinkDescriptor, ForceManyBodyDescriptor, ForceCenterDescriptor, ForceCollideDescriptor, ForceXDescriptor, ForceYDescriptor, ForceLayoutResult, ForceLayoutNode, ForceLayoutLink, and ForceLinkLineage.

Tidy-tree exports are treeLayout, TreeOrientation, TreeNodeContext, TreeNodeComparator, TreeNodeSeparation, TreeLayoutPathOptions, TreeLayoutParentOptions, TreeLayoutOptions, TreeLayoutNode, TreeLayoutLink, and TreeLayoutResult.