TanStack
API Reference

Chart Spec

Every static definition and dynamic chart builder resolves to a ChartSpec. The spec owns chart composition, scale factories or fixed scale instances, and presentation.

ts
type ChartSpec<TMarks extends readonly ChartMark[]> = {
  marks: TMarks
  guides?: boolean
  color?: ChartColorOptions
  gradients?: readonly ChartLinearGradient[]
  clip?: boolean
  margin?: number | Partial<ChartMargin>
  theme?: Partial<ChartTheme>
} & ([ChartMarkScaleX<TMarks[number]>] extends [never]
  ? { x?: null }
  : { x: ChartAxisOptions }) &
  ([ChartMarkScaleY<TMarks[number]>] extends [never]
    ? { y?: null }
    : { y: ChartAxisOptions })

Properties

PropertyRequiredMeaning
marksYesOrdered mark layers. Later scene nodes paint after earlier ones.
xConditionalRequired when a mark materializes x; omitted otherwise.
yConditionalRequired when a mark materializes y; omitted otherwise.
guidesNoSet to false to suppress both axes, grid lines, titles, and their implicit margins.
colorNoShared categorical or quantitative color scale and optional legend.
gradientsNoLinear-gradient resources consumed by the default SVG and Canvas renderers.
clipNoClips the marks group to the resolved inner chart bounds in the default SVG and Canvas renderers.
marginNoLocks all margins with a number or selected sides with a partial object. Omitted sides are measured automatically.
themeNoOverrides default foreground, muted, grid, background, or palette tokens.

The detailed option contracts live in Scales, guides, and color. Mark-specific channels and defaults live in the mark reference.

Marks and layer order

marks is the grammar's composition unit:

ts
import { areaY, defineChart, lineY, ruleY } from '@tanstack/charts'
import { scaleUtc } from 'd3-scale'
import { scaleLinear } from '@tanstack/charts-scales/linear'

const definition = defineChart({
  marks: [
    areaY(rows, { x: 'date', y: 'value', fillOpacity: 0.12 }),
    ruleY([target], {
      stroke: '#dc2626',
      strokeWidth: 1.5,
      strokeDasharray: '4 2',
    }),
    lineY(rows, { x: 'date', y: 'value', points: true }),
  ],
  x: { scale: scaleUtc },
  y: { scale: scaleLinear, grid: true },
})

Each mark materializes channels for scale resolution, then emits renderer- neutral scene nodes and optional interaction points. Marks may use different datum types in the same spec. Their inferred datum types become a union in interaction callbacks.

A data-less crosshair emits only transient focus-guide presentation. Place it before the first ordinary mark for an underlay or after ordinary marks for an overlay; it contributes no scale domain values or interaction points.

Built-in marks infer stable keys from a unique primitive top-level id, nested data.id, or mark-owned positional candidate. Supply key when none is unique. Mark IDs default from layer order; set id explicitly when a mark must retain identity while its order changes.

Conditional positional axes

Each axis used by the marks is required. Supply a compatible factory for an inferred domain or a configured instance for a fixed domain:

ts
const axes = {
  x: { scale: scaleUtc },
  y: { scale: scaleLinear },
}

Omit an unused dimension:

ts
const horizontalThresholds = defineChart({
  marks: [ruleY([25, 50, 75])],
  y: { scale: scaleLinear().domain([0, 100]) },
})

axis: false hides an axis but does not remove its scale. Scene compilation still guards untyped consumers that omit or null an axis used by a mark.

Guides and margins

Guide visibility and geometry are separate:

  • x.axis: false or y.axis: false hides one axis.
  • guides: false hides all guides and removes their implicit margin.
  • Omitted margin sides are measured from ticks, rotation, titles, edge overhang, color legends, and Cartesian text marks.
  • margin: 0 locks every side to zero.
  • margin: { left: 80 } locks only the left side.

Automatic margins contain guide and text-mark labels unless the side is locked or the plot is clipped; they do not choose a collision policy. Control dense guide labels with the scale's tick behavior, ticks, format, or tickRotate.

Clip and gradient resources

clip and gradients are scene data consumed by the default SVG and Canvas renderers:

ts
const definition = defineChart({
  marks,
  x,
  y,
  clip: true,
  gradients: [
    {
      id: 'revenue',
      y1: 1,
      y2: 0,
      stops: [
        { offset: 0, color: '#2563eb', opacity: 0.08 },
        { offset: 1, color: '#2563eb', opacity: 0.72 },
      ],
    },
  ],
})

Reference a declared gradient from a mark paint as url(#revenue). idPrefix scopes generated resource IDs when multiple charts share a document. See Rendering and export.

Theme

The complete default theme is exported as defaultChartTheme:

ts
interface ChartTheme {
  foreground: string
  muted: string
  grid: string
  background: string
  palette: readonly string[]
}

theme is partial. The palette is replaced as one value rather than merged by index. The default palette uses CSS custom-property fallbacks:

css
.dashboard {
  --ts-chart-1: #38bdf8;
  --ts-chart-2: #fb7185;
  --ts-chart-3: #4ade80;
}

Because the default foreground and guide colors use currentColor, charts inherit light and dark mode without a JavaScript theme switch. Override theme tokens when the application needs an explicit visual system.