Skip to content

Charts

Line Chart

A trend across ordered numeric or date values, with visible axes and an exact-value table.

When to use it: Use it when the spacing and order of measurements are meaningful, especially over time.

On this page

Example

Loading example…
Line Chart.tsxtsx
import {
  ChartDescription,
  ChartTable,
  ChartTitle,
  ChartTooltip,
  LineChart,
  LineChartPlot,
  LineChartPoint,
} from "@comp0/react";

const revenue = [
  { x: 1, y: 18 },
  { x: 2, y: 31 },
  { x: 3, y: 27 },
  { x: 4, y: 42 },
] as const;

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

export function Example() {
  return (
    <LineChart
      values={revenue}
      xLabel="Quarter"
      yLabel="Revenue"
      formatX={formatQuarter}
      formatY={formatRevenue}
      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">
        Quarterly revenue
      </ChartTitle>
      <LineChartPlot
        aria-label="Line chart showing quarterly revenue"
        className="mx-auto mt-5 aspect-square w-full max-w-md overflow-visible"
      >
        {({ path, points }) => (
          <>
            <path
              aria-hidden="true"
              d={path}
              fill="none"
              className="stroke-teal-600 dark:stroke-teal-400"
              strokeWidth="3"
              vectorEffect="non-scaling-stroke"
            />
            {points.map((point) => (
              <LineChartPoint 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-teal-600 group-data-active:stroke-zinc-950 dark:fill-zinc-950 dark:stroke-teal-400 dark:group-data-active:stroke-white"
                  strokeWidth="2"
                  vectorEffect="non-scaling-stroke"
                />
              </LineChartPoint>
            ))}
          </>
        )}
      </LineChartPlot>
      <ChartDescription className="mt-5 text-sm text-zinc-600 dark:text-zinc-400">
        Revenue finished at its highest point after a small third-quarter decline.
      </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">Quarterly revenue values</caption>
        <thead>
          <tr>
            <th scope="col">Quarter</th>
            <th scope="col">Revenue</th>
          </tr>
        </thead>
        <tbody>
          {revenue.map((quarter) => (
            <tr key={quarter.x}>
              <th scope="row">{formatQuarter(quarter.x)}</th>
              <td>{formatRevenue(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" />
    </LineChart>
  );
}

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

    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. LineChartPlot / LineChartPoint

    SVG line and points 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 LineChart with strictly increasing x values and labels for both axes.

  2. 2

    Add the supporting parts

    Add ChartTitle and LineChartPlot; render each point with LineChartPoint 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
    <LineChart values={revenue} xLabel="Quarter" yLabel="Revenue">
      <ChartTitle>Quarterly revenue</ChartTitle>
      <LineChartPlot aria-label="Line chart showing quarterly revenue">
        {({ path, points }) => (
          <>
            <path d={path} />
            {points.map((point) => (
              <LineChartPoint key={point.index} point={point}>
                <circle cx={point.x} cy={point.y} />
              </LineChartPoint>
            ))}
          </>
        )}
      </LineChartPlot>
      <ChartDescription>Revenue finished at its highest point.</ChartDescription>
      <ChartTable>
        <caption>Quarterly revenue values</caption>
        <thead>
          <tr>
            <th scope="col">Quarter</th>
            <th scope="col">Revenue</th>
          </tr>
        </thead>
        <tbody>
          {revenue.map((quarter) => (
            <tr key={quarter.x}>
              <th scope="row">Q{quarter.x}</th>
              <td>${quarter.y}k</td>
            </tr>
          ))}
        </tbody>
      </ChartTable>
      <ChartTooltip />
    </LineChart>;

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 LineChartPlot a concise aria-label that identifies the chart type, subject, and direction of change.
  • Wrap custom circles in LineChartPoint 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 visible points, line styles, or direct labels when color alone would not distinguish the series.

API reference

Importtsx
import { ChartDescription, ChartTable, ChartTitle, ChartTooltip, LineChart, LineChartPlot, LineChartPoint } from "@comp0/react";

LineChart

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.

LineChartPlot / LineChartPoint

DOM element

SVG line and points 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: LineChartPlotState) => ReactNodeCustom renderer receiving the line path and ordered point geometry.
pointChartPointPoint state passed from the plot to LineChartPoint.
LineChartPoint 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.

LineChartPlot / LineChartPoint

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

ChartTooltip

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

Keep exploring