TanStack
TanStack

Charts

A chart grammar you don't have to outgrow.

Build the chart you need from typed data, marks, channels, and D3 scales—then render responsive SVG in React or vanilla TypeScript.

Docs
Data
productSignals · 8 rows
Marks
areaY + ruleY + lineY + dot + text
Scales
linear × linear
const releases = productSignals.filter(
  (row) => row.product === 'Form' || row.product === 'Store',
)

export const kineticLayeredChart = defineChart({
  marks: [
    areaY(productSignals, {
      id: 'hero-area',
      x: 'month',
      y1: 'previous',
      y2: 'forecast',
      key: 'id',
      fill: '#3aa3c4',
      fillOpacity: 0.24,
      curve: d3Curve(curveMonotoneX),
    }),
    ruleY([70], {
      id: 'hero-rule',
      stroke: '#e06e49',
      strokeWidth: 2,
      strokeDasharray: '7 7',
    }),
    lineY(productSignals, {
      id: 'hero-line',
      x: 'month',
      y: 'value',
      key: 'id',
      stroke: '#61adbf',
      strokeWidth: 3,
      curve: d3Curve(curveMonotoneX),
    }),
    dot(productSignals, {
      id: 'hero-points',
      x: 'month',
      y: 'value',
      z: 'segment',
      key: 'id',
      stroke: '#9cd5e2',
      strokeWidth: 2,
      r: 4,
    }),
    text(releases, {
      id: 'hero-labels',
      x: 'month',
      y: 'value',
      text: 'product',
      key: 'id',
      fill: '#ffffff',
      fontSize: 11,
      fontWeight: 650,
      dy: -18,
    }),
  ],
  x: {
    scale: scaleLinear().domain([1, 8]),
    ticks: 4,
    format: (month) => `M${month}`,
  },
  y: {
    scale: scaleLinear().domain([30, 100]),
    ticks: 4,
    grid: true,
  },
  theme: kineticDarkTheme,
})

Examples, not presets.

Browse all examples
Time-series line with missing-value gaps
Multi-series line with end labels
Temperature range band with boundary lines
Stacked multi-series time area
Sorted vertical bars
Horizontal ranking with long labels
Grouped bars
Stacked bars
Bubble scatterplot
Histogram
Labeled ordinal heatmap
Anscombe quartet small multiples
Interval timeline
Point estimates with error bars
Grouped boxplot
Ranked lollipop chart
Dumbbell comparison
Cumulative histogram
Moving-average time-series line
Normalized 100% stacked area
Streamgraph
Bollinger band
Quantitative binned heatmap
Calendar heatmap
Diverging Likert responses
Parallel coordinates
Candlestick chart
Waterfall bridge
Two-period slopegraph
Scatterplot with linear regression
Directed change arrows
Actual versus forecast difference
Pointer-selected tooltip
Grouped X tooltip
Tidy hierarchy tree
Delaunay spatial network
Filled topographic contours
Point density contours
Regional GeoJSON choropleth
Force-directed dependency network
Responsive waffle unit chart
Two-dimensional vector field
Hexagonally binned density
Framed guide-free scatterplot
Empirical cumulative distribution
Faceted distributions
Beeswarm distribution
Log-scale scatterplot
Bump chart of changing ranks
Indexed multi-series performance
Directed connected scatterplot
Scatterplot with marginal histograms
Annotated time-series extrema
Grouped reducer bars
Lag-one autocorrelation
Percentile ribbon
Ridgeline density comparison
Violin distribution comparison
Marimekko market composition
Voronoi nearest-point interaction
Layered area, bar, line, and scatter chart
Population pyramid
Stacked and adjacent bars
Many-point scatter performance
Bundle size treemap
Simple radar chart
Categorical pie chart
Categorical donut chart
Partial-circle gauge
Snapped axis pointer with grouped tooltip
Interactive series legend
Linked chart and data table selection
Focus and context time window
Pinned tooltip with a nested chart
Scrollable resource timeline lanes
Streaming window preservation
Synchronized cursors across views
Free two-dimensional cursor
Monthly-snapped brush selection
Wheel zoom and pan time window
Timeline playback scrubber
Editable event range
Pie with outside labels
Donut with center total
Rounded donut with gaps
Nested donut rings
Nightingale rose chart
Threshold gauge with needle
Comparative radar chart
Concentric radial bars
Sunburst hierarchy
Equal Earth world choropleth
Projected proportional-symbol map
Orthographic globe with graticule
Great-circle route map
Numeric polar line
Numeric polar scatter
World country choropleth
United States state choropleth
Standard world projection gallery

Agents write the chart. TypeScript checks the result.

Every example compiles under strict TypeScript. Fields, datum types, domains, tooltips, and focus callbacks stay connected to the source datum; the type suite rejects eight invalid definitions.

Example prompt

Plot monthly revenue against retention. Size each account by seats, color it by segment, add tooltips, and keep the Account type in every callback.

Account → SVG
dot(accounts, {
 x: 'monthlyRevenue',
 y: 'retention',
 r: 'seats',
 z: 'segment',
 key: 'id',
})

Account health

Example account dataset · Q2 2026

Which high-revenue accounts need retention attention?

EnterpriseGrowthSMBBubble size = seats
Illustrative account data. Horizontal position shows monthly revenue in thousands of dollars, vertical position shows 90-day retention, color distinguishes SMB, Growth, and Enterprise segments, and bubble area represents account count.

Make it look like your product.

Same data and scales. CSS variables, themes, mark props, custom tooltips, or your own renderer control the rest.

Editorial

Product

Terminal

Complete SVG charts. 18.5–19.2 kB gzip.

Measured minified and gzipped from one-series charts using the vanilla renderer. Granular imports leave unrelated marks and D3 modules out.

Common chart bundles

Minified + gzip

Weekly revenue by product

One definition · two container sizes

Wide container900 × 420
Illustrative weekly revenue data from January through April 2026. Three product lines share the same date and revenue scales.
Compact container420 × 420
Illustrative weekly revenue data from January through April 2026. Three product lines share the same date and revenue scales.

Layer marks over shared scales.

Area, rules, lines, points, and labels share one coordinate system. The definition beside the chart is the whole composition.

Weekly activation rate

Jan–May 2026 · illustrative product telemetry

78%

above 70% goal

ActivationExpected rangeGoalReleases
Illustrative weekly activation data from January through May 2026. The actual rate rises from 48 to 78 percent, compared with an expected range and a 70 percent goal. Onboarding v2 and Invite flow are marked as release events.
activation-chart.ts
import { curveMonotoneX, scaleLinear, scaleUtc } from 'd3'
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: '#3aa3c4',
      fillOpacity: 0.2,
      curve: d3Curve(curveMonotoneX),
    }),
    ruleY([70], {
      id: 'activation-goal',
      stroke: '#e06e49',
      strokeOpacity: 0.95,
      strokeWidth: 2,
      strokeDasharray: '7 7',
    }),
    lineY(weeks, {
      id: 'activation-line',
      x: 'date',
      y: 'activation',
      key: 'id',
      stroke: '#61adbf',
      strokeWidth: 3.25,
      points: true,
      curve: d3Curve(curveMonotoneX),
    }),
    dot(releases, {
      id: 'activation-events',
      x: 'date',
      y: 'activation',
      key: 'id',
      r: 6,
      fill: '#ffffff',
      stroke: '#3aa3c4',
      strokeWidth: 3,
    }),
    text(releases, {
      id: 'activation-event-labels',
      x: 'date',
      y: 'activation',
      text: 'label',
      key: 'id',
      fill: '#ffffff',
      fontSize: 12,
      fontWeight: 650,
      dy: -21,
    }),
  ],
  x: {
    scale: scaleUtc().domain([weeks[0]!.date, weeks.at(-1)!.date]),
    label: 'Week ending',
    format: (value) => monthDay.format(value),
    grid: false,
  },
  y: {
    scale: scaleLinear().domain([40, 82]),
    label: 'Activation rate (%)',
    format: (value) => `${Math.round(value)}%`,
    grid: true,
    ticks: 5,
  },
  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.

Choose the chart by the question you're answering.

All 100 examples

Partners

Gold
Lovable
CodeRabbit
Cloudflare
Railway
Netlify
Silver
AG Grid
OpenRouter
WorkOS
SerpApi
Clerk
Bronze
Prisma
Unkey
Sentry
Electric
OSS Sponsors

Sponsors get special perks like private discord channels, priority issue requests, and direct support!