Skip to content

Charts

Bar Chart

A categorical comparison with visible axes and the same exact values in a native table.

When to use it: Use it to compare amounts across categories, especially when labels are long or ranking matters.

On this page

Example

Loading example…
Bar Chart.tsxtsx
import {
  BarChart,
  BarChartBar,
  BarChartPlot,
  ChartDescription,
  ChartTable,
  ChartTitle,
  ChartTooltip,
} from "@comp0/react";

const performance = [
  { label: "Reliability", value: 96 },
  { label: "Accessibility", value: 91 },
  { label: "Interaction", value: 84 },
  { label: "Rendering", value: 78 },
] as const;

const colors = [
  "fill-teal-600 dark:fill-teal-400",
  "fill-sky-600 dark:fill-sky-400",
  "fill-violet-600 dark:fill-violet-400",
  "fill-amber-500 dark:fill-amber-400",
] as const;

const formatPercent = (value: number) => `${value}%`;

export function Example() {
  return (
    <BarChart
      values={performance}
      categoryLabel="Measure"
      valueLabel="Target met"
      formatValue={formatPercent}
      className="w-full max-w-2xl rounded-lg has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-4 has-[:focus-visible]:outline-teal-600 dark:has-[:focus-visible]:outline-teal-400"
    >
      <ChartTitle className="text-base font-semibold text-zinc-950 dark:text-zinc-50">
        Performance against target
      </ChartTitle>
      <BarChartPlot
        aria-label="Horizontal bar chart comparing performance against target"
        className="mx-auto mt-5 aspect-square w-full max-w-md overflow-visible"
      >
        {(bar) => (
          <BarChartBar bar={bar} className="group outline-none">
            <rect
              x={bar.x}
              y={bar.y}
              width={bar.width}
              height={bar.height}
              className={`${colors[bar.index]} stroke-transparent group-data-active:stroke-zinc-950 dark:group-data-active:stroke-white`}
              rx="1.5"
              strokeWidth="2"
              vectorEffect="non-scaling-stroke"
            />
          </BarChartBar>
        )}
      </BarChartPlot>
      <ChartDescription className="mt-5 text-sm text-zinc-600 dark:text-zinc-400">
        Reliability and accessibility are closest to the target; rendering has the largest gap.
      </ChartDescription>
      <ChartTable className="mt-4 w-full border-collapse text-left text-sm [&_td]:border-t [&_td]:border-zinc-200 [&_td]:py-2 [&_th]:border-zinc-200 [&_th]:py-2 dark:[&_td]:border-zinc-800 dark:[&_th]:border-zinc-800">
        <caption className="sr-only">Performance against target values</caption>
        <thead>
          <tr>
            <th scope="col">Measure</th>
            <th scope="col">Target met</th>
          </tr>
        </thead>
        <tbody>
          {performance.map((measure) => (
            <tr key={measure.label}>
              <th scope="row">{measure.label}</th>
              <td>{formatPercent(measure.value)}</td>
            </tr>
          ))}
        </tbody>
      </ChartTable>
      <ChartTooltip className="pointer-events-none z-50 rounded-md bg-zinc-950 px-2 py-1 text-sm text-white shadow-lg dark:bg-zinc-50 dark:text-zinc-950" />
    </BarChart>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. BarChart

    Native figure sharing categorical values, labels, and formatting with every part. Owns a DOM element.

  2. ChartTitle

    Native figcaption that visibly names the figure. Owns a DOM element.

  3. BarChartPlot / BarChartBar

    Horizontal SVG bars with visible category and numeric axes. Owns a DOM element.

  4. ChartDescription

    Visible prose summarizing the important comparison. Owns a DOM element.

  5. ChartTable

    Native table composed with an explicit caption, headers, and rows from the chart values. Owns a DOM element.

  6. ChartTooltipOptional

    Optional floating value label shown on hover or focus. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Start BarChart with labelled values plus visible category and value axis labels.

  2. 2

    Add the supporting parts

    Add ChartTitle and BarChartPlot; wrap each rendered bar in BarChartBar so one bar is tabbable and arrow keys reveal the rest.

  3. 3

    Make the behavior clear

    Add ChartDescription and ChartTable for persistent context and exact values; optionally add ChartTooltip for pointer and keyboard details.

    Exampletsx
    <BarChart values={performance} categoryLabel="Measure" valueLabel="Target met">
      <ChartTitle>Performance against target</ChartTitle>
      <BarChartPlot aria-label="Horizontal bar chart comparing performance against target">
        {(bar) => (
          <BarChartBar bar={bar}>
            <rect x={bar.x} y={bar.y} width={bar.width} height={bar.height} />
          </BarChartBar>
        )}
      </BarChartPlot>
      <ChartDescription>Rendering has the largest gap.</ChartDescription>
      <ChartTable>
        <caption>Performance values</caption>
        <thead>
          <tr>
            <th scope="col">Measure</th>
            <th scope="col">Target met</th>
          </tr>
        </thead>
        <tbody>
          {performance.map((measure) => (
            <tr key={measure.label}>
              <th scope="row">{measure.label}</th>
              <td>{measure.value}%</td>
            </tr>
          ))}
        </tbody>
      </ChartTable>
      <ChartTooltip />
    </BarChart>;

Keyboard

Enters the chart at its current bar and leaves with one more Tab.
Moves to the next bar without wrapping.
Moves to the previous bar without wrapping.
Home
Moves to the first bar.
End
Moves to the last bar.
Esc
Dismisses an open ChartTooltip.

Forms and accessibility

Charts are descriptive content and do not create form values.

Accessibility checklist

  • Keep the category and value axis labels visible; tick labels should use the same units as the table.
  • Give BarChartPlot a concise aria-label that identifies the chart type and subject.
  • Wrap custom marks in BarChartBar to expose one roving tab stop; each bar receives a formatted category-and-value name.
  • ChartTooltip is an optional visual enhancement for hover and focus; never make it the only source of a value.
  • Include ChartTable when exact values matter; its native headers preserve category and value relationships during screen-reader navigation.
  • Do not use color as the only way to distinguish bars; the visible category axis must identify each one.

API reference

Importtsx
import { BarChart, BarChartBar, BarChartPlot, ChartDescription, ChartTable, ChartTitle, ChartTooltip } from "@comp0/react";

BarChart

DOM element

Native figure sharing categorical values, labels, and formatting with every part.

PropTypeDescription
valuesreadonly CategoricalChartValue[]Category labels and finite numeric values.
categoryLabelstringVisible heading for the vertical category axis.
valueLabelstringVisible heading for the horizontal numeric axis.
formatValue(value: number) => stringFormats numeric axis ticks and table cells.

ChartTitle

DOM element

Native figcaption that visibly names the figure.

BarChartPlot / BarChartBar

DOM element

Horizontal SVG bars with visible category and numeric axes.

PropTypeDescription
aria-labelstringConcise text alternative naming the graphic and subject.
xMinnumberOptional finite horizontal scale bounds that must contain every value and zero.
xMaxnumberOptional finite horizontal scale bounds that must contain every value and zero.
xTickCountnumberVisible numeric-axis tick count; defaults to five.
children(bar: BarChartBarState) => ReactNodeCustom bar renderer receiving the category, value, and SVG geometry.
barBarChartBarStateBar state passed from the plot to BarChartBar.
BarChartBar childrenReactNodeSVG shapes grouped into one named, keyboard-reachable bar.

ChartDescription

DOM element

Visible prose summarizing the important comparison.

ChartTable

DOM element

Native table composed with an explicit caption, headers, and rows from the chart values.

PropTypeDescription
childrenReactNodeNative caption, thead, tbody, and optional tfoot markup.

ChartTooltip

OptionalDOM element

Optional floating value label shown on hover or focus.

PropTypeDescription
placementPopoverPlacementSide of the active mark; defaults to "top".
offsetnumberDistance from the active mark; defaults to eight pixels.
childrenReactNode | (details: ChartValueDetails) => ReactNodeCustom content receiving the active bar's formatted details.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

BarChartPlot / BarChartBar

Style hookMeaning
[data-value]The numeric value represented by this bar group.
[data-active]The bar currently reached by pointer or keyboard.

ChartTooltip

Style hookMeaning
[data-open]A value tooltip is visible.

Keep exploring