Data

Chart

A chart drawn in the theme's colors, which people can use without seeing it: a figure named by its title, a summary for screen readers, and a table of every value (visible with showTable). The plot is a tab stop, and the arrow keys move its tooltip from point to point. Each series is labeled in a legend with its own marker shape, so color is never the only cue.

Recipes per month

January to June

Bar chart of Cooked and Saved by Month, 6 points. The table that follows lists every value.

Recipes per month
MonthCookedSaved
January124
February189
March157
April2211
May268
June3114

Install

Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):

npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13

Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):

npx shadcn@latest add ourostack/ouro-ui/chart#v1.2.13

Or, with the @ouro namespace set up:

npx shadcn@latest add @ouro/ouro-base @ouro/chart

Its tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):

npx shadcn@latest add ourostack/ouro-ui/chart#v1.2.13 ourostack/ouro-ui/chart-tests#v1.2.13
import { Chart } from "@/components/ouro/chart/chart";

Paths use shadcn's default @/ alias; the CLI rewrites them to yours.

Example

<Chart
  title="Recipes cooked per month"
  data={months}
  categoryKey="month"
  config={{ cooked: { label: "Cooked" }, saved: { label: "Saved", tone: "chart-2" } }}
/>

Usage

Pass type for bars, lines or areas, one per series in config. For anything else, pass a recharts chart as children with <ChartTooltip /> and <ChartLegend /> inside it, as in shadcn's charts; it draws with var(--color-<key>). The marks Chart draws itself appear at once; your own <Line> or <Bar> keeps recharts' draw-in unless you pass isAnimationActive={false}.

Built on recharts, which Bundlephobia measures at about 150 KB gzip (566 KB minified) for the whole package; a bundler that drops what Chart does not import adds less to the page that renders it. Load it with next/dynamic or React.lazy when the chart is below the fold.

Colors come from the theme's --chart-1 to --chart-5, which must reach 3:1 against the background for the marks to be seen (WCAG's non-text contrast).

Use instead when

  • Use Progress instead for one value out of a total, and a table alone when readers need exact figures more than the shape of the data.

Props

Type: ChartProps<Row>.

categoryKey (required)
Type: keyof Row & string
The key in each row that names its category: the x axis and the table's first column.
children
Type: ReactNode
A recharts chart (<LineChart>) for anything type cannot draw. It gets data, the title as its name and the theme colors as var(--color-<key>); add <ChartTooltip /> and <ChartLegend /> inside it.
className
Type: string
Layout only: the chart's height (h-64, aspect-square), margins. The plot is 16:9 until you set a height. Appearance classes warn in development.
config (required)
Type: ChartConfig<Row>
The series to draw, by their key in each row, with their labels and tones.
data (required)
Type: readonly Row[]
The rows to draw, one per category (a month, a recipe).
description
Type: ReactNode
A line under the title, such as the period: "January to June".
formatValue
Type: ((value: number, series: string) => string)
Default: "1,250"
Writes a value for the axis, the tooltip and the table, for other languages or units. series is the value's key in config, and "" for the y axis, which all series share.
hideTitle
Type: boolean
Hides the title visually; screen readers still hear it.
ref
Type: Ref<HTMLElement>
Reaches the rendered element.
showTable
Type: boolean
Shows the data table under the chart. It is always there for screen readers.
summary
Type: ((info: ChartSummaryInfo) => string)
Default: (info) => "Bar chart of Cooked and Saved by Month, 3 points. The table that follows lists every value."
What screen readers hear about the chart after its title, for other languages.
title (required)
Type: string
What the chart shows: "Recipes cooked per month". Its name for screen readers, shown above it. Required.
type
Type: "area" | "line" | "bar"
Default: "bar"
What to draw when there are no children: bars, lines or areas, one per series.

Native attributes

Also accepts the attributes in React's HTMLAttributes (id, aria-*, event handlers), passed to the rendered element, except those named on this page.

className is for layout only

Pass margins, width and grid placement (mt-4, w-full, col-span-2). Your classes always win over Ouro UI's: Chart merges className last with cn (tailwind-merge), so a class that conflicts with its own replaces it.layout-classes

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.

Alignment classes (text-center) warn too: set alignment on a parent element.

Types

ChartConfig

The series a chart draws, by their key in each row, as in shadcn's ChartConfig. Each key also names a CSS variable, --color-<key>, for recharts children (fill="var(--color-cooked)"). A label for categoryKey names the category column.

type ChartConfig<Row extends object = Record<string, unknown>> = { [Key in keyof Row & string]?: ChartSeries };

ChartSeries

One series in a chart's config: its label and the theme tone it is drawn in.

interface ChartSeries {
  /** What the series is called in the legend, the tooltip and the data table: "Recipes cooked". */
  label: string;
  /**
   * The theme color: `chart-1` to `chart-5`, or a tone that means something (`destructive`
   * for failures). Without it, series take `chart-1`, `chart-2` and so on, in order.
   */
  tone?: ChartTone | (string & { 'Use "chart-1" to "chart-5", "primary", "destructive", "success" or "warning"': never }) | undefined;

  /** Not a prop. Colors come from the theme: `{ label: "Cooked", tone: "chart-1" }`. */
  color?: { 'Colors come from the theme. Use { label: "Cooked", tone: "chart-1" }': never };
  /** Not a prop. Light and dark colors come from the theme's chart tokens: `{ label: "Cooked", tone: "chart-1" }`. */
  theme?: { 'Colors come from the theme. Use { label: "Cooked", tone: "chart-1" }': never };
}

Claim sources