TanStack
API Reference

Chart Definition API

defineChart

ts
import { defineChart } from '@tanstack/charts'

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,
): DynamicChartDefinition<InferredDatum, InferredX, InferredY>

function defineChart<const TSpec>(
  config: DynamicChartConfig<TSpec>,
): DynamicChartDefinition<InferredDatum, InferredX, InferredY>

function defineChart<TDatum, TXValue, TYValue>(
  definition: ChartDefinition<TDatum, TXValue, TYValue>,
  options: ChartDefinitionOptions<TDatum, TXValue, TYValue>,
): ChartDefinition<TDatum, TXValue, TYValue>

Static definitions

Use a static definition when its data and visual options are already known:

ts
const definition = defineChart({
  marks: [lineY(rows, { x: 'date', y: 'value' })],
  x: { scale: scaleUtc },
  y: { scale: scaleLinear, nice: true, grid: true },
  focus: 'group-x',
  tooltip: {
    anchor: 'group-center',
    placement: ['top', 'right', 'left', 'bottom'],
  },
})

Responsive definitions

Use a configuration object when the spec depends on the resolved chart surface:

ts
const definition = defineChart({
  animate: true,
  chart: ({ width }) => ({
    marks: [barY(rows, { x: 'category', y: 'value' })],
    x: { scale: scaleBand },
    y: {
      scale: scaleLinear,
      nice: true,
      ticks: width < 480 ? 4 : 7,
      grid: true,
    },
  }),
})

The builder receives:

PropertyTypeMeaning
widthnumberCurrent full surface width
heightnumberCurrent full surface height
themeChartThemeDefault build-time theme tokens

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, maxFocusDistance, spatialIndex, animate, keyboard, and tooltip. These options belong to both static and responsive definitions. Hosts and framework adapters do not override them.

Tooltip placement policy stays with the definition, including portal: true when the surface must escape clipped chart ancestors. Framework-only content composition remains an adapter prop, slot, snippet, or template.

DynamicChartConfig<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.

Identity and updates

A definition captures application values. Its identity is the application update boundary: keep it stable until a captured value changes.

tsx
const definition = useMemo(() => {
  const ranked = rankRows(rows, metric)

  return defineChart(({ width }) => ({
    marks: [barX(ranked, { x: 'value', y: 'label' })],
    x: {
      scale: scaleLinear,
      nice: true,
      ticks: 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
  theme: ChartTheme
}

type ChartDefinition<TDatum, TXValue, TYValue> =
  | StaticChartDefinition<TDatum, TXValue, TYValue>
  | DynamicChartDefinition<TDatum, TXValue, TYValue>

isDynamicChartDefinition narrows the union to a builder definition.