Skip to content

Charts

Area Chart

A trend whose filled area emphasizes magnitude, with visible axes and an exact-value table.

When to use it: Use it for an ordered series when the amount beneath the line matters as much as its direction.

On this page

Example

Loading example…
Area Chart.tsxtsx
import {
  AreaChart,
  AreaChartPlot,
  AreaChartPoint,
  ChartDescription,
  ChartTable,
  ChartTitle,
  ChartTooltip,
} from "@comp0/react";

const activeAccounts = [
  { x: 1, y: 12 },
  { x: 2, y: 24 },
  { x: 3, y: 38 },
  { x: 4, y: 51 },
] as const;

const formatQuarter = (value: number | Date) => `Q${value}`;
const formatAccounts = (value: number) => `${value}k`;

export function Example() {
  return (
    <AreaChart
      values={activeAccounts}
      xLabel="Quarter"
      yLabel="Active accounts"
      formatX={formatQuarter}
      formatY={formatAccounts}
      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">
        Active accounts
      </ChartTitle>
      <AreaChartPlot
        aria-label="Area chart showing active accounts by quarter"
        className="mx-auto mt-5 aspect-square w-full max-w-md overflow-visible"
      >
        {({ areaPath, linePath, points }) => (
          <>
            <path
              aria-hidden="true"
              d={areaPath}
              className="fill-sky-500/25 dark:fill-sky-400/25"
            />
            <path
              aria-hidden="true"
              d={linePath}
              fill="none"
              className="stroke-sky-600 dark:stroke-sky-400"
              strokeWidth="2"
              vectorEffect="non-scaling-stroke"
            />
            {points.map((point) => (
              <AreaChartPoint key={point.index} point={point} className="group outline-none">
                <circle cx={point.x} cy={point.y} r="4" className="fill-transparent" />
                <circle
                  cx={point.x}
                  cy={point.y}
                  r="1.8"
                  className="fill-white stroke-sky-600 group-data-active:stroke-zinc-950 dark:fill-zinc-950 dark:stroke-sky-400 dark:group-data-active:stroke-white"
                  strokeWidth="2"
                  vectorEffect="non-scaling-stroke"
                />
              </AreaChartPoint>
            ))}
          </>
        )}
      </AreaChartPlot>
      <ChartDescription className="mt-5 text-sm text-zinc-600 dark:text-zinc-400">
        Active accounts grew every quarter and more than quadrupled across the year.
      </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">Active account values</caption>
        <thead>
          <tr>
            <th scope="col">Quarter</th>
            <th scope="col">Active accounts</th>
          </tr>
        </thead>
        <tbody>
          {activeAccounts.map((quarter) => (
            <tr key={quarter.x}>
              <th scope="row">{formatQuarter(quarter.x)}</th>
              <td>{formatAccounts(quarter.y)}</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" />
    </AreaChart>
  );
}

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

    Native figure sharing ordered coordinates, axis labels, and formatting with every part. Owns a DOM element.

  2. ChartTitle

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

  3. AreaChartPlot / AreaChartPoint

    SVG filled area and boundary positioned against visible x and y axes. Owns a DOM element.

  4. ChartDescription

    Visible prose summarizing the important trend. 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 AreaChart with strictly increasing x values and labels for both axes.

  2. 2

    Add the supporting parts

    Add ChartTitle and AreaChartPlot; render each point with AreaChartPoint so one point is tabbable and horizontal 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
    <AreaChart values={accounts} xLabel="Quarter" yLabel="Active accounts">
      <ChartTitle>Active accounts</ChartTitle>
      <AreaChartPlot aria-label="Area chart showing active accounts by quarter">
        {({ areaPath, points }) => (
          <>
            <path d={areaPath} />
            {points.map((point) => (
              <AreaChartPoint key={point.index} point={point}>
                <circle cx={point.x} cy={point.y} />
              </AreaChartPoint>
            ))}
          </>
        )}
      </AreaChartPlot>
      <ChartDescription>Active accounts grew every quarter.</ChartDescription>
      <ChartTable>
        <caption>Active account values</caption>
        <thead>
          <tr>
            <th scope="col">Quarter</th>
            <th scope="col">Active accounts</th>
          </tr>
        </thead>
        <tbody>
          {accounts.map((quarter) => (
            <tr key={quarter.x}>
              <th scope="row">Q{quarter.x}</th>
              <td>{quarter.y}k</td>
            </tr>
          ))}
        </tbody>
      </ChartTable>
      <ChartTooltip />
    </AreaChart>;

Keyboard

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

Forms and accessibility

Charts are descriptive content and do not create form values.

Accessibility checklist

  • Keep both axis labels visible and format ticks with the same units used in ChartTable.
  • Give AreaChartPlot a concise aria-label that identifies the chart type, subject, and direction of change.
  • Wrap custom circles in AreaChartPoint to expose one roving tab stop; each point receives its formatted x and y coordinates as a name.
  • ChartTooltip is an optional visual enhancement for hover and focus; never make it the only source of a value.
  • Include ChartTable when exact coordinates matter so the full series remains available for structured review.
  • Use sufficient contrast for the boundary line and do not rely on fill color alone to communicate the series.

API reference

Importtsx
import { AreaChart, AreaChartPlot, AreaChartPoint, ChartDescription, ChartTable, ChartTitle, ChartTooltip } from "@comp0/react";

AreaChart

DOM element

Native figure sharing ordered coordinates, axis labels, and formatting with every part.

PropTypeDescription
valuesreadonly CartesianChartValue[]Finite y values paired with strictly increasing numeric or Date x values.
xLabelstringVisible headings for the horizontal and vertical axes.
yLabelstringVisible headings for the horizontal and vertical axes.
formatX(value) => stringFormatters shared by axis ticks and table cells.
formatY(value) => stringFormatters shared by axis ticks and table cells.

ChartTitle

DOM element

Native figcaption that visibly names the figure.

AreaChartPlot / AreaChartPoint

DOM element

SVG filled area and boundary positioned against visible x and y axes.

PropTypeDescription
aria-labelstringConcise text alternative naming the graphic and trend.
yMinnumberOptional finite vertical scale bounds.
yMaxnumberOptional finite vertical scale bounds.
yTickCountnumberVisible vertical-axis tick count; defaults to five.
children(state: AreaChartPlotState) => ReactNodeCustom renderer receiving area and line paths, the baseline, and ordered points.
pointChartPointPoint state passed from the plot to AreaChartPoint.
AreaChartPoint childrenReactNodeSVG shapes grouped into one named, keyboard-reachable point.

ChartDescription

DOM element

Visible prose summarizing the important trend.

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

AreaChartPlot / AreaChartPoint

Style hookMeaning
[data-active]The point currently reached by pointer or keyboard.

ChartTooltip

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

Keep exploring