TanStack
API Reference

Scales, Guides, and Color

Pass a D3 scale factory when its domain should come from mark channels. Pass a scale instance when the domain is fixed application state. TanStack Charts copies the resolved scale, assigns its responsive pixel range, and uses that copy for marks, ticks, and interaction. A supplied instance is never mutated.

For the architectural boundary and guidance on selecting granular D3 modules, read Scales and D3. This page documents only the TanStack Charts contract around those primitives.

Positional scale factories

The common path passes the D3 factory directly:

ts
import { scaleLinear, scaleUtc } from 'd3-scale'

const x = {
  scale: scaleUtc,
  nice: true,
  label: 'Date',
}

const y = {
  scale: scaleLinear,
  nice: true,
  label: 'Revenue',
  grid: true,
}

The chart creates a fresh scale for each layout, derives its domain from every materialized channel bound to that axis, applies nice, and assigns the responsive range. Bar and area baselines contribute zero when their baseline is implicit. Empty channels retain the D3 factory's native domain.

Return a configured scale from a zero-argument factory for options that should be applied before domain inference:

ts
const x = {
  scale: () => scaleBand<string>().padding(0.2),
}

nice is an axis option because it must run after the inferred domain exists.

Fixed domains

Pass a scale instance when the domain is semantic application state:

ts
const y = {
  scale: scaleLinear().domain([0, 100]),
}

Instances retain:

  • its semantic domain
  • continuous, temporal, logarithmic, ordinal, or band mapping behavior
  • tick generation and default tick formatting
  • clamping, unknown values, interpolation, and padding configured by the caller

TanStack Charts owns:

  • factory-domain inference
  • the responsive pixel range
  • y-range orientation and optional axis reversal
  • centering values within a band
  • guide placement, label measurement, and margins

Scale copies make one configured scale safe to reuse across responsive scenes.

Axis options

ts
interface ChartAxisOptions<TValue extends ChartValue> {
  scale: ChartScale | ConfiguredScaleLike<TValue> | ChartScaleFactory<TValue>
  nice?: boolean | number
  guide?: boolean
  ticks?: number
  format?: (value: TValue) => string
  grid?: boolean
  label?: string
  reverse?: boolean
  tickRotate?: number
  labelOffset?: number
}
OptionDefaultMeaning
scaleRequiredD3 factory, configured instance, or advanced ChartScale.
nicefalseNice the resolved domain using the responsive or supplied tick count.
guidetrue for a non-null axisRenders the axis, ticks, title, and requested grid.
ticksResponsive targetSuggested tick count passed to the scale. It is not a guaranteed count.
formatScale formatter, then string conversionFormats tick values. Its value type is inferred from the marks.
gridfalse for x; true for yDraws grid rules at tick positions.
labelNoneAxis title.
reversefalseReverses the responsive pixel range without changing the caller's scale.
tickRotate0Rotates tick labels in degrees.
labelOffsetAutomaticOverrides the title's distance from the axis.

Set an axis to null only when no mark uses that positional scale. To keep the scale while hiding its guide, use guide: false.

Without an explicit ticks, the responsive target is clamp(2, floor(chart.width / 92), 8) for x and clamp(2, floor(chart.height / 48), 7) for y. The configured scale may return a different number of ticks.

Automatic guide layout

When a margin side is omitted, the scene compiler measures:

  • formatted tick glyph bounds
  • tick rotation
  • first and last label overhang
  • axis title bounds and offset
  • color legend height

The DOM host measures the inherited container font and relayouts after web fonts load. Static compilation uses deterministic estimates unless measureText is supplied.

ts
interface ChartTextMeasurer {
  (
    text: string,
    options: {
      fontSize: number
      fontWeight?: number
      anchor: 'start' | 'middle' | 'end'
      baseline: 'auto' | 'middle' | 'hanging'
    },
  ): {
    x: number
    y: number
    width: number
    height: number
  }
}

The returned x and y locate the painted glyph box relative to the requested anchor and baseline. Pass the measurer through a host, adapter, runtime render, or createChartScene layout options.

Advanced custom scales

ChartScale is the unchecked extension boundary for a nonstandard positional mapping:

ts
interface ChartScale {
  id: string
  resolve(context: ChartScaleResolveContext): ResolvedScale
}

type ChartScaleResolver = (context: ChartScaleResolveContext) => ResolvedScale

The resolver context contains id, all materialized values, the responsive range, the axis options, a target tickCount, and includeZero. It must return a complete ResolvedScale:

ts
interface ResolvedScale {
  id: string
  type: string
  domain: readonly ChartValue[]
  map(value: unknown): number
  ticks: readonly { value: ChartValue; label: string; position: number }[]
  bandwidth: number
}

Prefer a configured scale when it can express the mapping. A custom scale owns correct domains, finite mapping, ticks, formatting, bandwidth, and response to the supplied range.

Color

Marks with a z or color channel contribute values to one chart-level color scale.

ts
interface ConfiguredColorScaleLike<TValue extends ChartKey, TOutput> {
  (value: TValue): TOutput
  copy: () => ConfiguredColorScaleLike<TValue, TOutput>
  domain?: () => readonly TValue[]
  range?: () => readonly TOutput[]
}

interface ChartColorOptions {
  scale?: ConfiguredColorScaleLike<any, any> | ChartColorScaleFactory<any, any>
  type?: ChartColorScale
  domain?: readonly ChartKey[]
  range?: readonly string[]
  nice?: boolean | number
  legend?: ChartColorLegend
}
OptionDefaultMeaning
scaleNoneD3 factory with inference or configured instance with a fixed domain
typeNoneCustom color-scale resolver
domainObserved channel valuesDomain hint for factory, built-in, or custom resolution
rangeResolved theme paletteRange hint for the built-in or custom resolver
nicefalseNice a factory or configured continuous color scale
legendNoneLegend layout and scene renderer shown above the inner chart

Resolution order:

  1. color.scale, created or copied before use
  2. custom color.type
  3. the built-in ordinal scale using domain or observed channel values and range or the theme palette

A color-scale factory infers a continuous extent or distinct categorical domain from color channels. Multi-stop continuous ranges receive evenly spaced domain stops across that extent. A supplied scale instance retains its domain. Outputs are converted to strings. Optional domain() and range() methods provide legend metadata; when either method is absent, supply the corresponding domain or range option if a legend needs it. A custom ChartColorScale receives observed values, optional domain and range, and the resolved theme, then returns:

ts
interface ResolvedColorScale {
  type: string
  domain: readonly ChartKey[]
  range: readonly string[]
  map(value: ChartKey | null | undefined): string
}

ChartKey is string | number. On the built-in and configured-scale paths, a null group maps to the first range color or currentColor. A custom ChartColorScale owns its null mapping through ResolvedColorScale.map.

Categorical legend

ts
import { colorLegend } from '@tanstack/charts/legend'

colorLegend({
  label: 'Package',
  itemWidth: 120,
})
ts
interface ColorLegendOptions {
  label?: string
  itemWidth?: number
}

itemWidth defaults to 110 and is clamped to a minimum of 64. Items wrap to responsive columns above the chart. Labels are derived from the color-scale domain.

Gradient legend

ts
import { colorGradientLegend } from '@tanstack/charts/legend'

colorGradientLegend({
  label: 'Density',
  steps: 48,
  width: 240,
  format: (value) => value.toFixed(1),
})
ts
interface ColorGradientLegendOptions {
  label?: string
  steps?: number
  width?: number
  format?: (value: number) => string
}

steps defaults to 32 and is clamped to at least 2. width defaults to 240, uses at least 80 when space permits, and is finally capped by the inner chart width. The legend requires a numeric first and last color-domain value and throws for a nonnumeric domain.

Custom legends

ChartColorLegend separates layout from rendering:

ts
interface ChartColorLegend {
  height(itemCount: number, width: number): number
  render(context: ChartColorLegendContext): SceneNode
}

height reserves space before chart bounds are finalized. render receives the resolved colors, chart bounds, theme, and full width. Return one keyed scene node.

Theme and gradients

The built-in theme is described in Chart spec. Chart gradients are independent SVG resources:

ts
interface ChartLinearGradient {
  id: string
  x1?: number
  y1?: number
  x2?: number
  y2?: number
  stops: readonly {
    offset: number
    color: string
    opacity?: number
  }[]
}

Coordinates and offsets are normalized from 0 to 1 by the resource-aware renderer. Omitted coordinates form a vertical gradient from bottom to top. Use renderChartSvgWithResources and an idPrefix; see Rendering and export.