import { defineChart } from '@tanstack/charts'defineChart accepts a complete chart spec, a responsive configuration, or an existing definition plus replacement behavior:
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>Use a static definition when its data and visual options are already known:
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'],
},
})Use a configuration object when the spec depends on the resolved chart surface:
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:
| Property | Type | Meaning |
|---|---|---|
| width | number | Current full surface width |
| height | number | Current full surface height |
| theme | ChartTheme | Default build-time theme tokens |
width and height are controlled by the host. The builder can read them but does not return or own them.
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.
A definition captures application values. Its identity is the application update boundary: keep it stable until a captured value changes.
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.
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.