Skip to content

Charts

Column Chart

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

When to use it: Use it to compare a small set of short-labelled categories or emphasize change in magnitude.

On this page

Example

Loading example…
Column Chart.tsxtsx
import {
  ChartDescription,
  ChartTable,
  ChartTitle,
  ChartTooltip,
  ColumnChart,
  ColumnChartColumn,
  ColumnChartPlot,
} from "@comp0/react";

const traffic = [
  { label: "Direct", value: 42 },
  { label: "Search", value: 31 },
  { label: "Referrals", value: 18 },
  { label: "Social", value: 9 },
] 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 (
    <ColumnChart
      values={traffic}
      categoryLabel="Source"
      valueLabel="Share"
      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">
        Traffic by source
      </ChartTitle>
      <ColumnChartPlot
        aria-label="Column chart comparing traffic share by source"
        className="mx-auto mt-5 aspect-square w-full max-w-md overflow-visible"
      >
        {(column) => (
          <ColumnChartColumn column={column} className="group outline-none">
            <rect
              x={column.x}
              y={column.y}
              width={column.width}
              height={column.height}
              className={`${colors[column.index]} stroke-transparent group-data-active:stroke-zinc-950 dark:group-data-active:stroke-white`}
              rx="1.5"
              strokeWidth="2"
              vectorEffect="non-scaling-stroke"
            />
          </ColumnChartColumn>
        )}
      </ColumnChartPlot>
      <ChartDescription className="mt-5 text-sm text-zinc-600 dark:text-zinc-400">
        Direct visits are the largest source, while social accounts for less than one tenth.
      </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">Traffic source values</caption>
        <thead>
          <tr>
            <th scope="col">Source</th>
            <th scope="col">Share</th>
          </tr>
        </thead>
        <tbody>
          {traffic.map((source) => (
            <tr key={source.label}>
              <th scope="row">{source.label}</th>
              <td>{formatPercent(source.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" />
    </ColumnChart>
  );
}

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. ColumnChart

    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. ColumnChartPlot / ColumnChartColumn

    Vertical SVG columns 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 ColumnChart with labelled values plus visible category and value axis labels.

  2. 2

    Add the supporting parts

    Add ChartTitle and ColumnChartPlot; wrap each rendered column in ColumnChartColumn so one column 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
    <ColumnChart values={traffic} categoryLabel="Source" valueLabel="Share">
      <ChartTitle>Traffic by source</ChartTitle>
      <ColumnChartPlot aria-label="Column chart comparing traffic share by source">
        {(column) => (
          <ColumnChartColumn column={column}>
            <rect x={column.x} y={column.y} width={column.width} height={column.height} />
          </ColumnChartColumn>
        )}
      </ColumnChartPlot>
      <ChartDescription>Direct visits are the largest source.</ChartDescription>
      <ChartTable>
        <caption>Traffic source values</caption>
        <thead>
          <tr>
            <th scope="col">Source</th>
            <th scope="col">Share</th>
          </tr>
        </thead>
        <tbody>
          {traffic.map((source) => (
            <tr key={source.label}>
              <th scope="row">{source.label}</th>
              <td>{source.value}%</td>
            </tr>
          ))}
        </tbody>
      </ChartTable>
      <ChartTooltip />
    </ColumnChart>;

Keyboard

Enters the chart at its current column and leaves with one more Tab.
Moves to the next column without wrapping.
Moves to the previous column without wrapping.
Home
Moves to the first column.
End
Moves to the last column.
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 ColumnChartPlot a concise aria-label that identifies the chart type and subject.
  • Wrap custom marks in ColumnChartColumn to expose one roving tab stop; each column 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 columns; the visible category axis must identify each one.

API reference

Importtsx
import { ChartDescription, ChartTable, ChartTitle, ChartTooltip, ColumnChart, ColumnChartColumn, ColumnChartPlot } from "@comp0/react";

ColumnChart

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 horizontal category axis.
valueLabelstringVisible heading for the vertical numeric axis.
formatValue(value: number) => stringFormats numeric axis ticks and table cells.

ChartTitle

DOM element

Native figcaption that visibly names the figure.

ColumnChartPlot / ColumnChartColumn

DOM element

Vertical SVG columns with visible category and numeric axes.

PropTypeDescription
aria-labelstringConcise text alternative naming the graphic and subject.
yMinnumberOptional finite vertical scale bounds that must contain every value and zero.
yMaxnumberOptional finite vertical scale bounds that must contain every value and zero.
yTickCountnumberVisible numeric-axis tick count; defaults to five.
children(column: ColumnChartColumnState) => ReactNodeCustom column renderer receiving the category, value, and SVG geometry.
columnColumnChartColumnStateColumn state passed from the plot to ColumnChartColumn.
ColumnChartColumn childrenReactNodeSVG shapes grouped into one named, keyboard-reachable column.

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 column'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.

ColumnChartPlot / ColumnChartColumn

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

ChartTooltip

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

Keep exploring