This is the full developer documentation for Saas UI v3. # Area Compose `areaY` for the fill and `lineY` for the edge. Keep both marks on the same fields so they share scales. ```tsx 'use client' import { Chart, useChart } from '@saas-ui/charts' import { areaY, 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', mrr: 42000 }, { date: 'Feb', mrr: 45800 }, { date: 'Mar', mrr: 47600 }, { date: 'Apr', mrr: 51200 }, { date: 'May', mrr: 53800 }, { date: 'Jun', mrr: 58100 }, ] const compactCurrency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', notation: 'compact', maximumFractionDigits: 1, }) export const ChartArea = () => { const chart = useChart({ data, series: [{ name: 'mrr', label: 'MRR', color: 'indigo.solid' }], }) const definition = useMemo( () => chart.define({ marks: [ areaY(chart.data, { x: 'date', y: 'mrr', fill: chart.color('indigo.solid'), fillOpacity: 0.16, }), lineY(chart.data, { x: 'date', y: 'mrr', stroke: chart.color('indigo.solid'), strokeWidth: 2, }), ], x: { scale: () => scaleBand().padding(0.12), 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 ( ) } ``` ## Usage ```tsx import { Chart, useChart } from '@saas-ui/charts' import { areaY, lineY } from '@tanstack/charts' import { scaleBand } from '@tanstack/charts/scales/band' import { scaleLinear } from '@tanstack/charts/scales/linear' ``` ```tsx const definition = chart.define({ marks: [ areaY(chart.data, { x: 'date', y: 'mrr', fill: chart.color('indigo.solid'), fillOpacity: 0.16, }), lineY(chart.data, { x: 'date', y: 'mrr', stroke: chart.color('indigo.solid'), strokeWidth: 2, }), ], x: { scale: () => scaleBand().padding(0.12) }, y: { scale: scaleLinear, nice: true, grid: true }, }) ``` Use `y1` and `y2` on `areaY` for a range or forecast band. `chart.gradient()` builds token-aware linear gradients when you need a fade instead of a flat fill. # Bar List `BarList` is an HTML composition. It uses the same `useChart` instance, but lays bars out with Chakra instead of SVG marks. Use it when you want labels and values in the document flow. ```tsx 'use client' import { BarList, useChart, type BarListData } from '@saas-ui/charts' const data: BarListData[] = [ { name: 'Direct', value: 1240 }, { name: 'Organic', value: 890 }, { name: 'Referral', value: 640 }, { name: 'Social', value: 310 }, ] export const BarListBasic = () => { const chart = useChart({ data, series: [{ name: 'name', color: 'indigo.solid' }], }) return ( ) } ``` ## Usage ```tsx import { BarList, useChart } from '@saas-ui/charts' ``` ```tsx const chart = useChart({ data: [ { name: 'Direct', value: 1240 }, { name: 'Organic', value: 890 }, ], series: [{ name: 'name', color: 'indigo.solid' }], }) ``` Each row needs a `name` and a numeric `value`. Set the series `name` to `"name"` so `BarList.Bar` can read the series color. ## Anatomy ```tsx ``` - `BarList.Bar` draws the bar and optional hover tooltip - `BarList.Value` prints the compact-formatted number - `barSize` on `Root` sets the row height from a size token Pass a function to `BarList.Bar`'s `label` or `tooltip` props to customize the row content. ## Sorted Pass `sort` to `useChart` to rank the rows without mutating your source data. ```tsx 'use client' import { BarList, useChart, type BarListData } from '@saas-ui/charts' const data: BarListData[] = [ { name: 'Referral', value: 640 }, { name: 'Direct', value: 1240 }, { name: 'Social', value: 310 }, { name: 'Organic', value: 890 }, ] export const BarListSorted = () => { const chart = useChart({ data, sort: { by: 'value', direction: 'desc' }, series: [{ name: 'name', color: 'teal.solid' }], }) return ( Traffic ) } ``` ```tsx const chart = useChart({ data, sort: { by: 'value', direction: 'desc' }, series: [{ name: 'name', color: 'teal.solid' }], }) ``` # Bar Segment `BarSegment` splits a single bar into colored segments. Each datum needs a `name`, `value` and `color`. ```tsx 'use client' import { BarSegment, useChart } from '@saas-ui/charts' const data = [ { name: 'Starter', value: 70, color: 'indigo.solid' }, { name: 'Pro', value: 40, color: 'pink.solid' }, { name: 'Enterprise', value: 25, color: 'fg' }, ] export const BarSegmentBasic = () => { const chart = useChart({ data }) return ( ) } ``` ## Usage ```tsx import { BarSegment, useChart } from '@saas-ui/charts' ``` ```tsx const chart = useChart({ data: [ { name: 'Starter', value: 70, color: 'indigo.solid' }, { name: 'Pro', value: 40, color: 'pink.solid' }, { name: 'Enterprise', value: 25, color: 'fg' }, ], }) ``` ## Anatomy ```tsx ``` Segment width is the value's share of the total. `BarSegment.Label` and `BarSegment.Value` use the same flex basis so they line up with the bar. ## Legend `BarSegment.Legend` lists each segment with a swatch. Set `showValue` and `showPercent` to print the number and share. ```tsx 'use client' import { BarSegment, useChart } from '@saas-ui/charts' const data = [ { name: 'Starter', value: 70, color: 'indigo.solid' }, { name: 'Pro', value: 40, color: 'pink.solid' }, { name: 'Enterprise', value: 25, color: 'fg' }, ] export const BarSegmentWithLegend = () => { const chart = useChart({ data }) return ( ) } ``` ## Reference `BarSegment.Reference` draws a marker at an absolute value on the same scale, useful for a quota or limit. ```tsx 'use client' import { BarSegment, useChart } from '@saas-ui/charts' const data = [ { name: 'Used', value: 72, color: 'indigo.solid' }, { name: 'Available', value: 28, color: 'bg.muted' }, ] export const BarSegmentWithReference = () => { const chart = useChart({ data }) return ( ) } ``` # Bar Use TanStack's `barY` mark inside `chart.define()`. Resolve fills with `chart.color()` so bars follow the active appearance. ```tsx '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().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 ( ) } ``` ## Usage ```tsx 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' ``` ```tsx const definition = chart.define({ marks: [ barY(chart.data, { x: 'date', y: 'revenue', fill: chart.color('indigo.solid'), maxThickness: 20, radius: 2, }), ], x: { scale: () => scaleBand().padding(0.28), grid: false, axis: { line: false, ticks: { size: 0 } }, }, y: { scale: scaleLinear, nice: true, grid: true, }, }) ``` Categorical x values use `scaleBand`. Numeric y values use `scaleLinear`. `maxThickness` keeps bars from stretching on wide charts; `radius` rounds the corners. Use `barX` from `@tanstack/charts` for horizontal bars, and flip the scales so `y` is the band axis. ## Tooltip Pass `renderTooltipBody` on `Chart.Root` to format values. ```tsx '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().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 ( ( currency.format(Number(value))} /> )} /> ) } ``` See [bar and rect marks](https://tanstack.com/charts/latest/docs/reference/marks/bar-and-rect) for stacking, grouping, `y1` / `y2` baselines and the rest of the mark API. # Chart `Chart` is the SVG host. Pass it the instance from `useChart` and a definition from `chart.define()`. ```tsx '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().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 ( ) } ``` ## Usage ```tsx import { Chart, useChart } from '@saas-ui/charts' ``` ```tsx ``` `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. ```tsx '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().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 ( ( currency.format(Number(value))} /> )} /> ) } ``` ```tsx ( 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. ```tsx '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().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 ( ) } ``` ```tsx ``` 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. ```tsx '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 ( Customers {chart.getTotal('value')} ) } ``` # Line Use TanStack's `lineY` mark. Map the category to `x` and the metric to `y`, and stroke with a resolved token. ```tsx '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', active: 1240 }, { date: 'Feb', active: 1580 }, { date: 'Mar', active: 1490 }, { date: 'Apr', active: 1820 }, { date: 'May', active: 1760 }, { date: 'Jun', active: 2110 }, ] export const ChartLine = () => { const chart = useChart({ data, series: [{ name: 'active', label: 'Active users', color: 'indigo.solid' }], }) const definition = useMemo( () => chart.define({ marks: [ lineY(chart.data, { x: 'date', y: 'active', stroke: chart.color('indigo.solid'), strokeWidth: 2, points: true, }), ], x: { scale: () => scaleBand().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 ( ) } ``` ## Usage ```tsx 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' ``` ```tsx const definition = chart.define({ marks: [ lineY(chart.data, { x: 'date', y: 'active', stroke: chart.color('indigo.solid'), strokeWidth: 2, points: true, }), ], x: { scale: () => scaleBand().padding(0.12) }, y: { scale: scaleLinear, nice: true, grid: true }, }) ``` `points: true` draws a dot at each observation. For time series, swap the x scale for `scaleUtc` from `d3-scale` and pass `Date` values. ## Multiple series Add one `lineY` mark per series, or partition a long table with `z`. Declare matching `series` entries so the tooltip and legend can name each line. ```tsx '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().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 ( ) } ``` ```tsx const chart = useChart({ data, series: [ { name: 'desktop', label: 'Desktop', color: 'indigo.solid' }, { name: 'mobile', label: 'Mobile', color: 'pink.solid' }, ], }) ``` # Charts `@saas-ui/charts` is a Chakra token layer for [TanStack Charts](https://tanstack.com/charts). It resolves colors, spacing and typography from your theme, then you compose chart geometry with TanStack marks. The package ships the pieces you reuse on every chart, not a catalog of one-off chart components: - `useChart` — data, series, token helpers and `define()` - `Chart` — themed root, tooltip, legend and center overlay - `BarList` and `BarSegment` — HTML compositions for ranked and part-to-whole data ## Installation :::code-group ```bash [npm] npm i @saas-ui/charts @tanstack/charts ``` ```bash [yarn] yarn add @saas-ui/charts @tanstack/charts ``` ```bash [pnpm] pnpm add @saas-ui/charts @tanstack/charts ``` ```bash [bun] bun add @saas-ui/charts @tanstack/charts ``` ::: `@saas-ui/charts` and `@tanstack/charts` are peer dependencies. Charts also expect a Chakra / Saas UI provider so token lookups resolve. ## Usage Create a chart instance with `useChart`, build a TanStack definition with `chart.define()`, and render it through `Chart.Root`. ```tsx 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' const chart = useChart({ data, series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }], }) const definition = chart.define({ marks: [ barY(chart.data, { x: 'date', y: 'revenue', fill: chart.color('indigo.solid'), }), ], x: { scale: () => scaleBand().padding(0.28) }, y: { scale: scaleLinear, nice: true, grid: true }, }) return ( ) ``` ```tsx '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().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.color()`, `chart.size()` and `chart.spacing()` resolve Saas UI tokens to CSS values TanStack can paint. Pass token paths such as `indigo.solid` or `fg.muted`. ## Mental model TanStack Charts is a grammar of graphics: you pick marks (`barY`, `lineY`, `areaY`, polar arcs) and map your fields onto channels. `@saas-ui/charts` does not wrap those marks. It gives you a theme-aware `define()` so the same chart follows light and dark mode, and Chakra components for tooltip, legend and HTML chart layouts. See the [TanStack Charts docs](https://tanstack.com/charts) for the full mark and scale catalog. ## What's included | Export | Use it for | | --- | --- | | [useChart](/docs/charts/use-chart) | Data, series, formatting, token helpers, `define()` | | [Chart](/docs/charts/chart) | SVG charts: root, tooltip, legend, center | | [Bar](/docs/charts/bar), [Line](/docs/charts/line), [Area](/docs/charts/area), [Pie](/docs/charts/pie) | Common mark compositions | | [Bar List](/docs/charts/bar-list) | Ranked HTML bars | | [Bar Segment](/docs/charts/bar-segment) | Part-to-whole HTML bars | # Pie Pie and donut charts use TanStack's polar marks. `pie()` computes the angles, `polar()` establishes the layout, and `radialArc()` paints the slices. ```tsx 'use client' import { Chart, useChart } from '@saas-ui/charts' import { pie, polar, radialArc } from '@tanstack/charts/polar' import { useMemo } from 'react' const data = [ { name: 'Starter', value: 42 }, { name: 'Pro', value: 35 }, { name: 'Enterprise', value: 23 }, ] export const ChartPie = () => { 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', stroke: 'none', }), ], }), ], color: { domain: chart.data.map((item) => item.name), range: chart.palette, }, margin: 0, }) }, [chart]) return ( ) } ``` ## Usage ```tsx import { Chart, useChart } from '@saas-ui/charts' import { pie, polar, radialArc } from '@tanstack/charts/polar' ``` ```tsx const chart = useChart({ data, series: [ { name: 'Starter', color: 'indigo.solid' }, { name: 'Pro', color: 'pink.solid' }, { name: 'Enterprise', color: 'fg' }, ], }) const slices = pie(chart.data, { value: 'value', gapAngle: (2 * Math.PI) / 180, }) const definition = chart.define({ marks: [ polar({ marks: [ radialArc(slices, { key: 'name', color: 'name', stroke: 'none', }), ], }), ], color: { domain: chart.data.map((item) => item.name), range: chart.palette, }, margin: 0, }) ``` Series colors become `chart.palette`. Point the categorical color scale at that palette so slices stay in sync with the legend. ## Donut Set `innerRadius` on `radialArc` and overlay `Chart.Center` for a total. ```tsx '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 ( Customers {chart.getTotal('value')} ) } ``` ```tsx radialArc(slices, { key: 'name', color: 'name', innerRadius: ({ radius }) => radius * 0.68, stroke: 'none', }) ``` # useChart `useChart` is the single hook for every chart in `@saas-ui/charts`. It stores the data, resolves Chakra tokens, and returns helpers used by `Chart.Root`, `BarList` and `BarSegment`. ```tsx import { useChart } from '@saas-ui/charts' const chart = useChart({ data, series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }], }) ``` ## Options | Prop | Type | Description | | --- | --- | --- | | `data` | `T[]` | Rows the marks and compositions read from. | | `series` | `SeriesItem[]` | Named series for color, label, icon and tooltip matching. | | `sort` | `{ by, direction }` | Sort a copy of `data` by a numeric field. | ### Series ```tsx interface SeriesItem { name?: keyof T | string color?: ChartColor icon?: React.ReactNode label?: React.ReactNode } ``` `name` is matched against mark and tooltip payloads. `color` accepts a token path (`indigo.solid`) or any CSS color. When series omit colors, `useChart` falls back to `indigo.solid`, `pink.solid`, `teal.solid`, `orange.solid`, `purple.solid` and `fg`. ## Return value ### Data - `data` — the original rows, or a sorted copy when `sort` is set - `series` — the series you passed in - `groupBy(dataKey)` — group rows by a field - `getSeries(item)` — resolve the series for a tooltip point or datum - `getTotal(dataKey)`, `getMin(dataKey)`, `getMax(dataKey)` - `getValuePercent(dataKey, value, domain?)` — percent of the total, or of a custom `[min, max]` domain - `getPayloadTotal(payload)` — sum tooltip point values ### Tokens - `color(token)`, `size(token)`, `spacing(token)` — resolve theme tokens - `theme` — TanStack theme (`foreground`, `muted`, `grid`, `background`, `palette`) - `palette` — resolved series colors, used by pie and categorical scales - `cssVars` — CSS variables applied by `Chart.Root` for tooltip chrome and series colors - `gradient({ id, stops })` — build a linear gradient with token colors ### Definition `chart.define(spec)` wraps TanStack's `defineChart`. It injects the theme, disables SVG animation by default, and portals tooltips so they are not clipped by overflow. ```tsx const definition = chart.define({ marks: [ barY(chart.data, { x: 'date', y: 'revenue', fill: chart.color('indigo.solid'), }), ], x: { scale: () => scaleBand().padding(0.28) }, y: { scale: scaleLinear, nice: true, grid: true }, }) ``` Pass any TanStack chart spec: marks, scales, axes, `color`, `margin`, `gradients` or a responsive `chart({ width })` builder. See [chart definitions](https://tanstack.com/charts/latest/docs/concepts/chart-definitions) in the TanStack docs. ### Formatting - `formatNumber(options?)` — `Intl.NumberFormat` bound to the Chakra locale - `formatDate(options?)` — `Intl.DateTimeFormat` bound to the Chakra locale ### Highlighting - `highlightedSeries` / `setHighlightedSeries(name)` - `isHighlightedSeries(name)` - `getSeriesOpacity(name, fallback?)` — `1` for the highlighted series, `fallback` (default `0.2`) for the others `Chart.Legend` uses these to dim sibling series on hover or click. HTML compositions such as `BarList` and `BarSegment` use the same state for tooltips. ```tsx '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().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 ( ) } ``` # App Shell ## Anatomy ```ts import { AppShell } from '#components/ui/app-shell' ``` ```tsx } sidebar={} aside={} footer={