Chart
Theme-aware root, tooltip, legend and center overlay for TanStack charts.
Chart is the SVG host. Pass it the instance from useChart and a
definition from chart.define().
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', revenue: 12500 },
{ date: 'Feb', revenue: 15800 },
{ date: 'Mar', revenue: 14200 },
{ date: 'Apr', revenue: 16900 },
{ date: 'May', revenue: 13600 },
{ date: 'Jun', revenue: 19200 },
]
const compactCurrency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: 'compact',
maximumFractionDigits: 1,
})
export const ChartBar = () => {
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
maxThickness: 20,
radius: 2,
}),
],
x: {
scale: () => scaleBand<string>().padding(0.28),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: {
line: false,
ticks: {
size: 0,
format: (value: number) => compactCurrency.format(value),
},
},
},
}),
[chart],
)
return (
<Chart.Root
chart={chart}
definition={definition}
height={240}
ariaLabel="Monthly revenue"
/>
)
}
Usage
import { Chart, useChart } from '@saas-ui/charts'<Chart.Root
chart={chart}
definition={definition}
height={240}
ariaLabel="Monthly revenue"
>
<Chart.Legend />
</Chart.Root>Chart.Root applies the chart CSS variables, sets a landscape aspect ratio
unless you pass width and height, and renders the default
Chart.Tooltip unless you replace it with renderTooltipBody.
Always pass ariaLabel. TanStack Charts uses it for the accessible name of
the graphic.
Tooltip
The default tooltip lists the focused points with a color swatch, series label and formatted value. Override it when you need custom formatting, totals or a different layout.
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', revenue: 12500 },
{ date: 'Feb', revenue: 15800 },
{ date: 'Mar', revenue: 14200 },
{ date: 'Apr', revenue: 16900 },
{ date: 'May', revenue: 13600 },
{ date: 'Jun', revenue: 19200 },
]
const currency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
})
const compactCurrency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: 'compact',
maximumFractionDigits: 1,
})
export const ChartTooltip = () => {
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
maxThickness: 20,
radius: 2,
}),
],
x: {
scale: () => scaleBand<string>().padding(0.28),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: {
line: false,
ticks: {
size: 0,
format: (value: number) => compactCurrency.format(value),
},
},
},
}),
[chart],
)
return (
<Chart.Root
chart={chart}
definition={definition}
height={240}
ariaLabel="Monthly revenue"
renderTooltipBody={({ points }) => (
<Chart.Tooltip
points={points}
formatter={(value) => currency.format(Number(value))}
/>
)}
/>
)
}
<Chart.Root
chart={chart}
definition={definition}
height={240}
ariaLabel="Monthly revenue"
renderTooltipBody={({ points }) => (
<Chart.Tooltip
points={points}
formatter={(value) => currency.format(Number(value))}
/>
)}
/>Chart.Tooltip accepts title, hideLabel, hideIndicator,
hideSeriesLabel, showTotal, nameKey and a render function for full
control of each point.
Legend
Render Chart.Legend as a child of Chart.Root. It reads chart.series
and highlights a series on hover. Set interaction="click" to toggle the
highlight instead.
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { lineY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', desktop: 820, mobile: 420 },
{ date: 'Feb', desktop: 910, mobile: 670 },
{ date: 'Mar', desktop: 880, mobile: 610 },
{ date: 'Apr', desktop: 1040, mobile: 780 },
{ date: 'May', desktop: 980, mobile: 780 },
{ date: 'Jun', desktop: 1120, mobile: 990 },
]
export const ChartLineMultiple = () => {
const chart = useChart({
data,
series: [
{ name: 'desktop', label: 'Desktop', color: 'indigo.solid' },
{ name: 'mobile', label: 'Mobile', color: 'pink.solid' },
],
})
const definition = useMemo(
() =>
chart.define({
marks: [
lineY(chart.data, {
x: 'date',
y: 'desktop',
stroke: chart.color('indigo.solid'),
strokeWidth: 2,
points: true,
}),
lineY(chart.data, {
x: 'date',
y: 'mobile',
stroke: chart.color('pink.solid'),
strokeWidth: 2,
points: true,
}),
],
x: {
scale: () => scaleBand<string>().padding(0.12),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: { line: false, ticks: { size: 0 } },
},
}),
[chart],
)
return (
<Chart.Root
chart={chart}
definition={definition}
height={240}
ariaLabel="Sessions by device"
>
<Chart.Legend />
</Chart.Root>
)
}
<Chart.Root
chart={chart}
definition={definition}
height={240}
ariaLabel="Sessions by device"
>
<Chart.Legend />
</Chart.Root>Use align, orientation and spacing to change the layout. The legend
only renders when at least one series has a name.
Center
Chart.Center absolutely positions children over the chart. Use it for a
total or KPI in the middle of a donut.
Customers
100
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { pie, polar, radialArc } from '@tanstack/charts/polar'
import { Stack, Text } from '@chakra-ui/react'
import { useMemo } from 'react'
const data = [
{ name: 'Starter', value: 42 },
{ name: 'Pro', value: 35 },
{ name: 'Enterprise', value: 23 },
]
export const ChartPieWithCenter = () => {
const chart = useChart({
data,
series: [
{ name: 'Starter', color: 'indigo.solid' },
{ name: 'Pro', color: 'pink.solid' },
{ name: 'Enterprise', color: 'fg' },
],
})
const definition = useMemo(() => {
const slices = pie(chart.data, {
value: 'value',
gapAngle: (2 * Math.PI) / 180,
})
return chart.define({
marks: [
polar({
marks: [
radialArc(slices, {
key: 'name',
color: 'name',
innerRadius: ({ radius }) => radius * 0.68,
stroke: 'none',
}),
],
}),
],
color: {
domain: chart.data.map((item) => item.name),
range: chart.palette,
},
margin: 0,
})
}, [chart])
return (
<Chart.Root
chart={chart}
definition={definition}
height={240}
ariaLabel="Customers by plan"
>
<Chart.Center>
<Stack gap="0" align="center">
<Text textStyle="xs" color="fg.muted">
Customers
</Text>
<Text textStyle="lg" fontWeight="medium">
{chart.getTotal('value')}
</Text>
</Stack>
</Chart.Center>
</Chart.Root>
)
}