component — charts

Line Chart

A config-driven line chart on the quebi Chart wrapper, rendering one ink series per config key with styled axes, grid, tooltip, and an interactive legend. Requires the recharts package.

  • chart
  • line
  • recharts
  • data
  • graph
  • visualization

09 examples

Default

A config-driven line chart with two ink series, an interactive legend, and a tooltip.

Single series

One ink line driven entirely by the config.

No grid lines

Hide the cartesian grid for a cleaner surface.

Edge labels only

Show only the first and last X-axis labels for dense series.

Custom series via children

Pass children to take full manual control of the rendered lines while keeping the styled wrapper.

Tooltip formatters

`tooltipProps` reaches the tooltip's own `formatter` and `labelFormatter`. Both return strings or nodes, so the number goes through `formatNumber(value, locale)` — a bare `toLocaleString()` here formats against whatever locale the runtime has, which differs between the prerender and the browser.

A tooltip surface of your own

Pass an element to `tooltip` and it replaces `ChartTooltipContent` entirely — recharts clones it with the active payload.

Reference line, area and dot

`overlays` renders extra recharts children *next to* the series the config generates. Passing them as `children` would replace those series instead.

Axis labels and tick formatting

An axis label on each side, and a `valueFormatter` on the Y ticks. The formatter returns a string, which is why it calls `formatNumber(value, locale)` rather than a component.

source

line-chart.tsx

Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.

"use client"

import { useMemo } from "react"
import { Line, LineChart as LineChartPrimitive, type LineProps } from "recharts"
import type { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"
import {
  type BaseChartProps,
  CartesianGrid,
  Chart,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
  constructCategoryColors,
  DEFAULT_COLORS,
  getColorValue,
  valueToPercent,
  XAxis,
  YAxis,
} from "@/components/chart"

/**
 * LineChart — quebi design system
 *
 * A config-driven line chart built on the quebi `Chart` wrapper. It renders one
 * `<Line>` per `config` key with a styled grid, axes, tooltip, and an
 * interactive legend. Series colors come from the quebi palette and can be
 * overridden per key via the `config` prop. Pass `children` to take full manual
 * control of the rendered series.
 *
 * Requires the `recharts` npm package as a peer dependency.
 */

export interface LineChartProps<TValue extends ValueType, TName extends NameType>
  extends BaseChartProps<TValue, TName> {
  connectNulls?: boolean
  lineProps?: LineProps
  chartProps?: Omit<React.ComponentProps<typeof LineChartPrimitive>, "data" | "stackOffset">
}

export function LineChart<TValue extends ValueType, TName extends NameType>({
  data = [],
  dataKey,
  colors = DEFAULT_COLORS,
  connectNulls = false,
  type = "default",
  config,
  children,

  // Components
  tooltip = true,
  tooltipProps,

  legend = true,
  legendProps,

  intervalType = "equidistantPreserveStart",

  valueFormatter = (value: number) => value.toString(),

  // XAxis
  displayEdgeLabelsOnly = false,
  xAxisProps,
  hideXAxis = false,

  // YAxis
  yAxisProps,
  hideYAxis = false,

  hideGridLines = false,
  chartProps,
  lineProps,
  overlays,
  ...props
}: LineChartProps<TValue, TName>) {
  const configKeys = useMemo(() => Object.keys(config), [config])
  const categoryColors = useMemo(
    () => constructCategoryColors(configKeys, colors),
    [configKeys, colors],
  )

  const configEntries = useMemo(() => Object.entries(config), [config])

  return (
    <Chart config={config} data={data} dataKey={dataKey} {...props}>
      {({ onLegendSelect, selectedLegend }) => (
        <LineChartPrimitive
          onClick={() => {
            onLegendSelect(null)
          }}
          data={data}
          margin={{
            bottom: 0,
            left: 0,
            right: 0,
            top: 5,
          }}
          stackOffset={type === "percent" ? "expand" : undefined}
          {...chartProps}
        >
          {!hideGridLines && <CartesianGrid strokeDasharray="4 4" />}
          <XAxis
            hide={hideXAxis}
            displayEdgeLabelsOnly={displayEdgeLabelsOnly}
            intervalType={intervalType}
            {...xAxisProps}
          />
          <YAxis
            hide={hideYAxis}
            tickFormatter={type === "percent" ? valueToPercent : valueFormatter}
            {...yAxisProps}
          />

          {legend && (
            <ChartLegend
              content={typeof legend === "boolean" ? <ChartLegendContent /> : legend}
              {...legendProps}
            />
          )}

          {tooltip && (
            <ChartTooltip
              content={
                typeof tooltip === "boolean" ? <ChartTooltipContent accessibilityLayer /> : tooltip
              }
              {...tooltipProps}
            />
          )}

          {!children
            ? configEntries.map(([category, values]) => {
                const strokeOpacity = selectedLegend && selectedLegend !== category ? 0.1 : 1
                const color = getColorValue(values.color || categoryColors.get(category))

                return (
                  <Line
                    key={category}
                    dot={false}
                    name={category}
                    type="linear"
                    dataKey={category}
                    stroke={color}
                    style={
                      {
                        strokeOpacity,
                        strokeWidth: 2,
                        "--line-color": color,
                      } as React.CSSProperties
                    }
                    strokeLinejoin="round"
                    strokeLinecap="round"
                    connectNulls={connectNulls}
                    {...lineProps}
                  />
                )
              })
            : children}

          {overlays}
        </LineChartPrimitive>
      )}
    </Chart>
  )
}