TanStack
Getting Started

React Quick Start

Install the framework adapter, core grammar, React peers, and only the granular data and scale modules used by the chart:

shell
pnpm add @tanstack/charts @tanstack/react-charts react react-dom d3-scale
pnpm add -D @types/d3-scale @types/react @types/react-dom

The shared Scales and D3 page explains why scales and optional algorithms remain direct application dependencies.

Define a chart

Definitions are ordinary framework-independent TypeScript:

tsx
import { scaleBand, scaleLinear } from 'd3-scale'
import { barY, defineChart } from '@tanstack/charts'
import { Chart } from '@tanstack/react-charts'

const revenue = [
  { month: 'Jan', value: 42 },
  { month: 'Feb', value: 58 },
  { month: 'Mar', value: 76 },
  { month: 'Apr', value: 64 },
]

const revenueChart = defineChart({
  marks: [
    barY(revenue, {
      x: 'month',
      y: 'value',
    }),
  ],
  x: {
    scale: () => scaleBand().padding(0.18),
  },
  y: {
    scale: scaleLinear,
    nice: true,
    label: 'Revenue',
    grid: true,
  },
  tooltip: true,
})

export function RevenueChart() {
  return (
    <Chart definition={revenueChart} height={320} ariaLabel="Monthly revenue" />
  )
}

The definition infers the original row and semantic x/y types. Do not add component generics or cast the definition.

Responsive sizing

Use a fixed height with responsive width:

tsx
<Chart definition={revenueChart} height={320} ariaLabel="Monthly revenue" />

Or give the host a proportional box:

tsx
<Chart
  definition={revenueChart}
  aspectRatio={16 / 9}
  initialWidth={720}
  ariaLabel="Monthly revenue"
/>

The outer element fills its available width when width is absent. The adapter server-renders with initialWidth, then the shared DOM host measures the actual container after hydration. See React adapter.

Memoize live definitions

When a chart captures component values, memoize the complete definition:

tsx
import { useMemo } from 'react'

interface RevenueInput {
  rows: readonly { month: string; value: number }[]
  accent: string
}

export function LiveRevenue({ rows, accent }: RevenueInput) {
  const definition = useMemo(() => {
    return defineChart({
      marks: [
        barY(rows, {
          x: 'month',
          y: 'value',
          fill: accent,
        }),
      ],
      x: {
        scale: () => scaleBand().padding(0.18),
      },
      y: {
        scale: scaleLinear,
        nice: true,
      },
      animate: true,
      tooltip: true,
    })
  }, [rows, accent])

  return (
    <Chart
      definition={definition}
      height={320}
      ariaLabel="Live monthly revenue"
    />
  )
}

The dependency list owns application invalidation. The definition identity tells the chart host when captured values changed. See Chart Definition API.

Interaction callbacks

Callback types flow from the marks:

tsx
<Chart
  definition={revenueChart}
  height={320}
  ariaLabel="Monthly revenue"
  onFocusChange={(point) => {
    if (point) {
      console.log(point.datum.month, point.yValue)
    }
  }}
  onSelect={(point) => {
    if (point) openMonth(point.datum.month)
  }}
/>

The native tooltip is optional. Grouped focus, formatting, keyboard behavior, and application-owned interaction are documented in Focus and interaction.

Example

This catalog example uses multiple line layers and endpoint labels through the same React adapter:

Continue with the React adapter for lifecycle and SSR, the Chart reference for every prop, or the core API reference for definitions, marks, scales, and rendering.