@tanstack/charts
Version:
A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.
174 lines (138 loc) • 5.92 kB
Markdown
title: Chart Definition API
description: Reference static and responsive chart definitions, build context, and identity-based updates.
## `defineChart`
```ts
import { defineChart } from '@tanstack/charts'
import { tooltip } from '@tanstack/charts/tooltip'
import { portal } from '@tanstack/charts/tooltip/portal'
```
`defineChart` accepts a complete chart spec, a responsive configuration, or an
existing definition plus replacement behavior:
```ts
function defineChart<const TMarks, const TSpec>(
spec: TSpec,
): StaticChartDefinition<InferredDatum, InferredX, InferredY>
function defineChart<const TSpec>(
chart: (context: ChartBuildContext) => TSpec,
): ResponsiveChartDefinition<InferredDatum, InferredX, InferredY>
function defineChart<const TSpec>(
config: ResponsiveChartConfig<TSpec>,
): ResponsiveChartDefinition<InferredDatum, InferredX, InferredY>
function defineChart<TDefinition, TOptions>(
definition: TDefinition,
options: TOptions,
): Omit<TDefinition, keyof TOptions> & TOptions
```
## Static definitions
Use a static definition when its data and visual options are already known:
```ts
import { scaleUtc } from 'd3-scale'
import { scaleLinear } from '@tanstack/charts/scales/linear'
const definition = defineChart({
marks: [lineY(rows, { x: 'date', y: 'value' })],
x: { scale: scaleUtc },
y: { scale: scaleLinear, nice: true, grid: true },
focus: 'group-x',
tooltip: {
use: tooltip,
portal,
anchor: 'group-center',
placement: ['top', 'right', 'left', 'bottom'],
},
})
```
## Responsive definitions
Use a configuration object when the spec depends on the resolved chart
surface:
```ts
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
const definition = defineChart({
svgAnimation: true,
chart: ({ width }) => ({
marks: [barY(rows, { x: 'category', y: 'value' })],
x: { scale: scaleBand },
y: {
scale: scaleLinear,
nice: true,
axis: { ticks: { count: width < 480 ? 4 : 7 } },
grid: true,
},
}),
})
```
The builder receives:
| Property | Type | Meaning |
| -------------- | ------------ | ------------------------------------------------------------ |
| `width` | `number` | Current full surface width |
| `height` | `number` | Current full surface height |
| `defaultTheme` | `ChartTheme` | Platform defaults before the returned spec applies its theme |
`width` and `height` are controlled by the host. The builder can read them but
does not return or own them.
## Definition behavior
`ChartDefinitionOptions<TDatum, TXValue, TYValue>` contains `focus`,
`focusRing`, `selection`, `controls`, `cursor`, `maxFocusDistance`,
`spatialIndex`, `svgAnimation`, `pointer`, `keyboard`, and `tooltip`. These options
belong to both static and responsive definitions. Hosts and framework adapters
do not override them.
Each `ChartControl` resolves after final scales and plot bounds exist. It can
provide renderer-neutral fallback nodes and an optional host control. Control
IDs and host-control identities must be unique. Browser hosts remove a
control's fallback before painting and own its update, renderer replacement,
event containment, and teardown lifecycle. Static renderers keep the fallback.
`cursor` binds an application-owned controller in focus-snapped or free mode.
It is behavior, not a mark; add `crosshair(...)` when the cursor should have a
renderer-native visual guide. See
[Focus and Interaction](./focus-and-interaction.md#controlled-cursors).
Tooltip placement policy stays with the definition. Add the `portal` extension
when the surface must escape clipped chart ancestors. Framework-only content
composition remains an adapter prop, slot, snippet, or template.
`ResponsiveChartConfig<TSpec>` combines those options with the responsive `chart`
builder. The two-argument `defineChart(definition, options)` form creates a new
definition when a reusable base needs a different interaction policy.
Definitions carry an optional fourth tooltip-host type parameter:
`ChartDefinition<TDatum, TXValue, TYValue, TTooltipHost>`. `DomChartDefinition`
fixes that host to `"dom"`; DOM adapter exports expose it as their local
`ChartDefinition`. Adding a DOM or React Native tooltip therefore makes that
definition host-specific. A definition without a tooltip remains assignable to
either host. Deliberately widening it to the generic three-parameter
`ChartDefinition` erases that proof and is rejected by strict host props.
## Identity and updates
A definition captures application values. Its identity is the application
update boundary: keep it stable until a captured value changes.
```tsx
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
const definition = useMemo(() => {
const ranked = rows
.map((row) => ({ label: row.label, value: row[metric] }))
.sort((left, right) => right.value - left.value)
return defineChart(({ width }) => ({
marks: [barX(ranked, { x: 'value', y: 'label' })],
x: {
scale: scaleLinear,
nice: true,
axis: { ticks: { count: width < 480 ? 4 : 7 } },
},
y: {
scale: () => scaleBand().padding(0.1),
},
}))
}, [rows, metric])
```
Framework adapters use their native memoization primitive. Vanilla code
creates the next definition and passes it to `host.update`.
## Types
```ts
interface ChartBuildContext {
width: number
height: number
defaultTheme: ChartTheme
}
type ChartDefinition<TDatum, TXValue, TYValue> =
| StaticChartDefinition<TDatum, TXValue, TYValue>
| ResponsiveChartDefinition<TDatum, TXValue, TYValue>
```
`isResponsiveChartDefinition` narrows the union to a builder definition.