Install the framework adapter, core grammar, React peers, and only the granular data and scale modules used by the chart:
pnpm add @tanstack/charts @tanstack/react-charts react react-dom d3-scale
pnpm add -D @types/d3-scale @types/react @types/react-domThe shared Scales and D3 page explains why scales and optional algorithms remain direct application dependencies.
Definitions are ordinary framework-independent TypeScript:
import { scaleBand, scaleLinear } from 'd3-scale'
import { barY, defineChart } from '@tanstack/charts'
import { Chart } from '@tanstack/react-charts'
interface AlphabetRow {
letter: string
frequency: number
}
const alphabet: readonly AlphabetRow[] = [
{ letter: 'E', frequency: 0.12702 },
{ letter: 'T', frequency: 0.09056 },
{ letter: 'A', frequency: 0.08167 },
{ letter: 'O', frequency: 0.07507 },
{ letter: 'I', frequency: 0.06966 },
]
const percent = new Intl.NumberFormat('en-US', {
style: 'percent',
maximumFractionDigits: 1,
})
const letterFrequencyChart = defineChart({
marks: [
barY(alphabet, {
x: 'letter',
y: 'frequency',
}),
],
x: {
scale: () => scaleBand().padding(0.18),
},
y: {
scale: scaleLinear,
nice: true,
label: 'Frequency',
format: (value) => percent.format(value),
grid: true,
},
tooltip: true,
})
export function LetterFrequencyChart() {
return (
<Chart
definition={letterFrequencyChart}
height={320}
ariaLabel="English letter frequencies"
/>
)
}The definition infers the original row and semantic x/y types. Do not add component generics or cast the definition.
Use a fixed height with responsive width:
<Chart
definition={letterFrequencyChart}
height={320}
ariaLabel="English letter frequencies"
/>Or give the host a proportional box:
<Chart
definition={letterFrequencyChart}
aspectRatio={16 / 9}
initialWidth={720}
ariaLabel="English letter frequencies"
/>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.
When a chart captures component values, memoize the complete definition:
import { useMemo } from 'react'
interface LetterFrequencyInput {
rows: readonly AlphabetRow[]
accent: string
}
export function LiveLetterFrequency({ rows, accent }: LetterFrequencyInput) {
const definition = useMemo(() => {
return defineChart({
marks: [
barY(rows, {
x: 'letter',
y: 'frequency',
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="Filtered English letter frequencies"
/>
)
}The dependency list owns application invalidation. The definition identity tells the chart host when captured values changed. See Chart Definition API.
Callback types flow from the marks:
<Chart
definition={letterFrequencyChart}
height={320}
ariaLabel="English letter frequencies"
onFocusChange={(point) => {
if (point) {
console.log(point.datum.letter, point.yValue)
}
}}
onSelect={(point) => {
if (point) openLetter(point.datum.letter)
}}
/>The native tooltip is optional. Grouped focus, formatting, keyboard behavior, and application-owned interaction are documented in Focus and interaction.
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.