Components
Charts
The chart library in @repo/ui is a composable wrapper around recharts that applies the design token vocabulary consistently. All chart components are dark-theme compatible and use semantic CSS custom properties — never hardcoded colors.
Peer dependency: Add
recharts >=2.0.0to the consuming app's dependencies.@repo/uilists it as a peer dependency only — it does not bundle recharts.
Architecture
The system is split into three layers. The core layer provides a shared context, Card wrapper, and slot components. The wrapper layer adds recharts-specific chart types. A standalone component handles the specialized stacked-bar log pattern.
| File | Import path | Contents |
|---|---|---|
chart.tsx | @repo/ui/components/chart | Core context, ChartCard, ChartHeader, ChartContent, ChartFooter, ChartTooltip, ChartConfig type, useChart hook |
bar-chart.tsx | @repo/ui/components/bar-chart | BarChartCard — recharts BarChart wrapper |
line-chart.tsx | @repo/ui/components/line-chart | LineChartCard — recharts LineChart / AreaChart wrapper |
logs-bar-chart.tsx | @repo/ui/components/logs-bar-chart | LogsBarChart — compact stacked status bars |
ChartConfig
Every chart that uses ChartTooltip receives a config prop of type ChartConfig. The config maps each data key to a display label, an optional fixed color, and an optional theme-aware color map.
import type { ChartConfig } from '@repo/ui/components/chart'
// Fixed color (same in light and dark)
const config: ChartConfig = {
count: { label: 'Tasks', color: 'var(--brand-default)' },
}
// Theme-aware color (resolve automatically based on data-theme)
const config: ChartConfig = {
revenue: {
label: 'Revenue',
theme: {
light: 'hsl(152.9deg 60% 52.9%)',
dark: 'hsl(153.1deg 60.2% 52.7%)',
},
},
}When using theme colors, the current theme class on the root element determines which value is used. Use CSS custom properties via var(--brand-default) for colors that already adapt automatically through the token system — this is the preferred approach and avoids needing the theme key at all.
Core components
ChartCard
The root wrapper. Provides ChartContext with isLoading, isDisabled, and config. Renders as a Card from @repo/ui/components/card.
| Prop | Type | Default | Description |
|---|---|---|---|
isLoading | boolean | false | Propagates to ChartContent which renders a Skeleton instead of children |
isDisabled | boolean | false | Applies opacity-60 pointer-events-none to the whole card |
config | ChartConfig | {} | Passed to ChartTooltip automatically via context |
ChartHeader
A two-column row inside the card: a left column holds the title and optional metric summary, a right column holds an optional action element (e.g. a refresh button or time-range selector).
| Prop | Type | Description |
|---|---|---|
title | ReactNode | Chart title rendered as a small semibold label |
metric | ReactNode | Secondary line below the title — typically a summary stat or date range |
action | ReactNode | Right-aligned slot for buttons or controls |
ChartContent
The main chart area. Reads isLoading from context: if loading, renders a Skeleton at the specified height. If isEmpty is true, renders a centered empty state message. Otherwise renders children in a sized container.
| Prop | Type | Default | Description |
|---|---|---|---|
height | number | 224 | Pixel height of the chart area (and the Skeleton when loading) |
isEmpty | boolean | false | Shows centered empty state instead of children |
emptyMessage | string | 'No data available' | Text shown in empty state |
ChartFooter
A below-chart slot for captions, data source notes, or legend summaries. Renders as a CardFooter with text-foreground-lighter text-xs.
ChartTooltip
A recharts Tooltip with consistent dark-surface styling. Reads config from ChartContext to resolve labels and colors for each data key. Can be placed inside any recharts chart.
| Prop | Type | Description |
|---|---|---|
labelFormatter | (label: string) => string | Transform the x-axis label shown at the top of the tooltip |
valueFormatter | (value: number, name: string) => string | Format numeric values (e.g. add units, currency symbols) |
config | ChartConfig | Override the context config for this tooltip instance |
useChart
Hook that reads the nearest ChartContext. Use inside custom recharts content renderers or child components to access isLoading, isDisabled, and config without prop drilling.
import { useChart } from '@repo/ui/components/chart'
function CustomLegend() {
const { config } = useChart()
return (
<div>
{Object.entries(config).map(([key, { label, color }]) => (
<span key={key} style={{ color }}>{label}</span>
))}
</div>
)
}Bar chart
BarChartCard
A complete card with ChartCard, optional ChartHeader, a recharts BarChart inside ChartContent, and optional ChartFooter. Supports vertical bars (default) and horizontal bars.
import { BarChartCard } from '@repo/ui/components/bar-chart'
import type { ChartConfig } from '@repo/ui/components/chart'
const config: ChartConfig = {
count: { label: 'Tasks completed', color: 'var(--brand-default)' },
}
const data = [
{ week: 'Feb 3', count: 4 },
{ week: 'Feb 10', count: 7 },
{ week: 'Feb 17', count: 5 },
{ week: 'Feb 24', count: 11 },
]
<BarChartCard
title="Weekly velocity"
data={data}
dataKey="count"
config={config}
categoryKey="week"
height={224}
/>| Prop | Type | Default | Description |
|---|---|---|---|
data | DataPoint[] | — | recharts data array |
dataKey | string | string[] | — | Key(s) to render as bars. Pass an array for grouped bars. |
config | ChartConfig | — | Label and color mapping for each data key |
categoryKey | string | 'name' | Key used for the category axis |
layout | 'horizontal' | 'vertical' | 'horizontal' | horizontal = vertical bars (standard); vertical = horizontal bars (agent breakdown, rankings) |
colorKey | string | — | Property on each data point to use as per-bar fill (overrides config color) |
showGrid | boolean | false | Render a CartesianGrid using --border-default |
showYAxis | boolean | true | Show the value axis |
showXAxis | boolean | true | Show the category axis |
height | number | 224 | Chart area height in px |
syncId | string | — | recharts syncId for linked tooltips across charts |
isLoading | boolean | false | Shows Skeleton in place of chart |
title, metric, action, footer | ReactNode | — | Forwarded to ChartHeader / ChartFooter; omitting them suppresses those sections entirely |
Horizontal bars (rankings / agent breakdown)
<BarChartCard
title="Completions by agent"
data={agentData}
dataKey="count"
config={config}
categoryKey="label"
layout="vertical"
colorKey="color" // each data point has a .color field
showYAxis={true}
height={224}
/>Grouped bars
const config: ChartConfig = {
done: { label: 'Done', color: 'var(--brand-default)' },
blocked: { label: 'Blocked', color: 'var(--destructive-default)' },
}
<BarChartCard
data={data}
dataKey={['done', 'blocked']}
config={config}
categoryKey="week"
/>Line chart
LineChartCard
Wraps recharts LineChart (or AreaChart when showArea is true). Supports multiple lines via array dataKey.
import { LineChartCard } from '@repo/ui/components/line-chart'
const config: ChartConfig = {
tokens: { label: 'Tokens', color: 'var(--brand-default)' },
}
<LineChartCard
title="Token usage over time"
data={data}
dataKey="tokens"
config={config}
categoryKey="week"
showArea
height={224}
/>| Prop | Type | Default | Description |
|---|---|---|---|
dataKey | string | string[] | — | Key(s) to render as lines |
showArea | boolean | false | Switches to AreaChart with a gradient fill below each line |
showDots | boolean | false | Render static dots on every data point (hover dots are always shown) |
showGrid | boolean | false | Show horizontal grid lines |
showYAxis | boolean | true | Show the value (Y) axis |
showXAxis | boolean | true | Show the category (X) axis |
syncId | string | — | recharts syncId for linked charts |
height, isLoading, title, metric, action, footer | — | — | Same as BarChartCard |
Multi-line with area fills
const config: ChartConfig = {
input_tokens: { label: 'Input', color: 'var(--brand-default)' },
output_tokens: { label: 'Output', color: '#a78bfa' },
}
<LineChartCard
data={data}
dataKey={['input_tokens', 'output_tokens']}
config={config}
categoryKey="date"
showArea
showGrid
/>Logs bar chart
LogsBarChart
A compact stacked bar chart for visualising status counts over time. Inspired by the Supabase dashboard log viewer. Status colors are fixed semantic constants: ok, warning, error.
Unlike the other chart components, LogsBarChart is a standalone component — it does not use ChartCard internally. Embed it inside any container with the height you need.
import { LogsBarChart } from '@repo/ui/components/logs-bar-chart'
import type { LogsDataPoint } from '@repo/ui/components/logs-bar-chart'
const data: LogsDataPoint[] = [
{ timestamp: '2026-02-28T00:00:00Z', ok_count: 142, error_count: 3, warning_count: 8 },
{ timestamp: '2026-02-28T01:00:00Z', ok_count: 98, error_count: 0, warning_count: 2 },
{ timestamp: '2026-02-28T02:00:00Z', ok_count: 203, error_count: 11, warning_count: 4 },
]
// Compact (default h-24, no axis labels)
<LogsBarChart data={data} />
// With timestamps on the X axis
<LogsBarChart data={data} height={96} showTimestamps />
// Inside a card header
<Card>
<CardHeader>
<CardTitle>API Health — Last 24h</CardTitle>
</CardHeader>
<CardContent className="px-4 pb-4 pt-0">
<LogsBarChart data={data} />
</CardContent>
</Card>| Prop | Type | Default | Description |
|---|---|---|---|
data | LogsDataPoint[] | — | Array of { timestamp, ok_count, error_count, warning_count } |
height | number | 96 | Height in px |
showTimestamps | boolean | false | Render an X axis with locale-formatted HH:MM labels |
isLoading | boolean | false | Renders a Skeleton at the given height |
Composing a custom chart
Use the bare Chart provider (not ChartCard) when you need the context and ChartTooltip without the Card wrapper — for example, embedding a chart inside an existing panel.
import {
Chart,
ChartTooltip,
type ChartConfig,
} from '@repo/ui/components/chart'
import { ResponsiveContainer, LineChart, Line } from 'recharts'
const config: ChartConfig = {
value: { label: 'Score', color: 'var(--brand-default)' },
}
<Chart config={config} isLoading={loading} className="h-40 w-full">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={data}>
<ChartTooltip />
<Line type="monotone" dataKey="value" stroke="var(--brand-default)" strokeWidth={2} dot={false} />
</LineChart>
</ResponsiveContainer>
</Chart>Styling reference
All chart components use semantic tokens. The axis tick color is var(--foreground-lighter) at fontSize 11. Grid lines use var(--border-default). Tooltip background is bg-surface-200 with a border-border stroke and rounded-lg corners.
| Element | Token / class |
|---|---|
| Axis ticks | var(--foreground-lighter), fontSize: 11 |
| Axis lines / tick lines | Hidden (axisLine=false, tickLine=false) |
| Grid | var(--border-default), dashed 3 3 |
| Tooltip background | bg-surface-200 |
| Tooltip border | border-border rounded-lg |
| Bar radius (vertical) | [4, 4, 0, 0] top corners only |
| Bar radius (horizontal) | [0, 4, 4, 0] right corners only |
| Line stroke width | 2 |
| Hover dot radius | 4 |
| Primary chart color | var(--brand-default) |