Charts
A chart grammar you don't have to outgrow.
A typed, tree-shakable chart grammar for SVG and Canvas. Compose marks, views, scales, transforms, interactions, and motion with compact primitives or D3-compatible inputs.
Your next chart, already working.
A lot of chart. Not a lot of bundle.
Basic React line, 29 kB minified + gzip. SVG, compact scales, axes included.
SVG by default. Canvas where it earns its weight.
Move one paint-heavy mark to Canvas while axes, labels, focus, tooltips, responsive layout, hydration, and export keep working through one definition. Canvas and motion stay out of the default bundle until you import them.
Rendering and export referenceOne definition, three surface choices
import { canvasChartRenderer } from '@tanstack/charts/canvas'
import { defineChart, dot, lineY } from '@tanstack/charts'
const activity = defineChart({
marks: [
lineY(summary, {
x: 'time',
y: 'average',
}),
dot(events, {
x: 'time',
y: 'latency',
renderer: canvasChartRenderer,
}),
],
scales,
tooltip,
})Default SVG
NothingAxes, labels, marks, and focus render as accessible SVG. Tooltips use an accessible HTML live region.
Mixed SVG + Canvas
One mark optionOnly the dense mark paints to Canvas. SVG guides and shared interaction stay in place.
Full Canvas
One adapter importThe whole chart paints to Canvas while the definition and host callbacks stay the same.
The same definition also feeds the web adapters, vanilla DOM, static output, and the experimental React Native adapter.
Make it look like your product.
Same data, scales, and interactions. CSS variables, themes, mark props, and custom tooltip content make the chart belong to your product.
Editorial
Product
Terminal
Monokai
Add ranges, goals, and events without switching APIs.
The area, goal rule, activation line, release points, and labels each use the rows and channels they need, then share one coordinate system. The same definition mixes a compact linear scale with D3's UTC scale and monotone curve.
Weekly activation rate
Illustrative product telemetry
78%
above 70% goal
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { scaleUtc } from 'd3-scale'
import { curveMonotoneX } from 'd3-shape'
import {
areaY,
d3Curve,
defineChart,
dot,
lineY,
ruleY,
text,
} from '@tanstack/charts'
import { releases, weeks } from './activation-data'
import { activationTheme } from './activation-theme'
const monthDay = new Intl.DateTimeFormat('en-US', {
day: 'numeric',
month: 'short',
timeZone: 'UTC',
})
export const activationChart = defineChart({
marks: [
areaY(weeks, {
id: 'activation-range',
x: 'date',
y1: 'expectedLow',
y2: 'expectedHigh',
key: 'id',
fill: 'var(--activation-range)',
fillOpacity: 0.22,
curve: d3Curve(curveMonotoneX),
}),
ruleY([70], {
id: 'activation-goal',
stroke: 'var(--activation-goal)',
strokeOpacity: 0.95,
strokeWidth: 2,
strokeDasharray: '7 7',
}),
lineY(weeks, {
id: 'activation-line',
x: 'date',
y: 'activation',
key: 'id',
stroke: 'var(--activation-line)',
strokeWidth: 4.25,
points: true,
curve: d3Curve(curveMonotoneX),
}),
dot(releases, {
id: 'activation-events',
x: 'date',
y: 'activation',
key: 'id',
r: 6,
fill: 'var(--activation-bg)',
stroke: 'var(--activation-release)',
strokeWidth: 3,
}),
text(releases, {
id: 'activation-event-labels',
x: 'date',
y: 'activation',
text: 'label',
key: 'id',
fill: 'var(--activation-foreground)',
fontSize: 12,
fontWeight: 650,
dy: -21,
}),
],
scales: {
x: {
scale: scaleUtc().domain([weeks[0]!.date, weeks.at(-1)!.date]),
axis: {
label: 'Week ending',
ticks: { format: (value) => monthDay.format(value) },
},
grid: false,
},
y: {
scale: scaleLinear().domain([40, 82]),
axis: {
label: 'Activation rate (%)',
ticks: { count: 5, format: (value) => `${Math.round(value)}%` },
},
grid: true,
},
},
theme: activationTheme,
})TanStack Charts builds on Leland Wilkinson's grammar of graphics and the work of ggplot2, Vega-Lite, and Observable Plot. Its marks-and-channels API is most directly inspired by Observable Plot, but the runtime is an independent implementation.
Partners
Sponsors get special perks like private discord channels, priority issue requests, and direct support!