Acronis UIKit
Components

LineChart

A typed line chart — single or multi-series, with curve and dashed variants.

Usage

import { LineChart } from '@acronis-platform/ui-react';
import type { ChartConfig } from '@acronis-platform/ui-react';

LineChart is a typed composition over the shared Chart primitives. Give it data, a per-series config, the series to plot (dataKeys), and the category key (xKey) — it renders a themed recharts line chart (tooltip, legend, axes, grid included), so you don't hand-compose recharts children.

Series colors come from the palette prop, not from config — see Palettes. Each config entry maps a data key to a label and an optional tone that re-points that one series within the palette. LineChart resolves them into the --color-<key> custom properties its lines stroke from. Single vs multi line simply follows from how many dataKeys you plot.

Examples

A multi-series line chart with a themed tooltip and legend:

const data = [
  { month: 'Jan', desktop: 186, mobile: 80 },
  { month: 'Feb', desktop: 305, mobile: 200 },
  { month: 'Mar', desktop: 237, mobile: 120 },
];

const config = {
  desktop: { label: 'Desktop' },
  mobile: {
    label: 'Mobile'
  },
} satisfies ChartConfig;

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  className="h-[320px] w-[560px]"
/>;

Plot a single series by passing one dataKey:

<LineChart config={config} data={data} dataKeys={['desktop']} xKey="month" />

Change the interpolation with curve. Seven types are available, from most to least literal about the data:

curveReads as
linearStraight segments between points.
monotone (default)Smoothed, never overshooting a point's value.
naturalA natural cubic spline — smoother, but it may overshoot, so read it as a trend.
basisA B-spline that need not pass through the points at all; the smoothest, least literal option.
stepRight angles, changing value at the midpoint between two points.
stepBeforeThe same, changing at the leading point.
stepAfterThe same, changing at the trailing point.
<LineChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  curve="stepAfter"
/>

Dash the strokes with lineStyle="dashed":

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  lineStyle="dashed"
/>

Toggle the chrome with showGrid / showTooltip / showLegend, enable per-point dots with showDots, and bridge null gaps with connectNulls:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  showDots
  connectNulls
/>

dotSize sets the dot radius (3px by default; the hover dot is always 2px larger). showActiveDot decides the hover dot on its own — unset it follows showDots. Pass showDots to show static dots, showActiveDot for a hover-only emphasis on a dot-less line, or both for the full treatment:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop']}
  xKey="month"
  showDots
  dotSize={6}
/>

Overlay a previous-period series for QoQ/YoY comparison by listing it in comparisonKeys — those series render dashed, dimmed, and dot-less behind the current-period lines, keeping their own config color:

<LineChart
  config={config}
  data={data}
  dataKeys={['thisYear', 'lastYear']}
  comparisonKeys={['lastYear']}
  xKey="month"
/>

Shade the gap between a current and comparison series with deltaBands — pairs of [current, comparison] keys draw a dimmed area (the delta) behind the lines:

<LineChart
  config={config}
  data={data}
  dataKeys={['thisYear', 'lastYear']}
  comparisonKeys={['lastYear']}
  deltaBands={[['thisYear', 'lastYear']]}
  xKey="month"
/>

A band takes the current key's color and curveType, so it keeps hugging the line it belongs to. A pair whose two series set different curveTypes can only follow the current one.

Add axis titles and a y-axis unit suffix with xAxisLabel / yAxisLabel / yUnit (the x-axis is categorical). The titles inherit the theme token:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  xAxisLabel="Month"
  yAxisLabel="Response time"
  yUnit="ms"
/>

Projections

Mark a boundary at which the data transitions from actuals to forecast with projectionStart. Pass the xKey value where the projection zone begins — from that point onward, x-axis tick labels render in the disabled text color and series strokes switch to a dashed pattern:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  projectionStart="Apr"
/>

The boundary point is included in both the solid and dashed segments so they connect visually. When projectionStart doesn't match any value in the data the chart renders normally.

Per-series overrides

lineSettings restyles one series without touching the others — keyed by data key, and any field left out falls back to the chart-wide prop:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile', 'tablet']}
  xKey="month"
  lineSettings={{
    desktop: { strokeWidth: 3 },
    mobile: { dashed: true, showDots: false },
    tablet: { curveType: 'stepAfter', dotSize: 5 },
  }}
/>

Each entry takes color, strokeWidth, dashed, curveType, showDots, dotSize, and the label controls (showLabel, labelPosition) — so one series can carry value labels while the rest stay clean.

A series listed in comparisonKeys keeps its dashed, dimmed, dot-less treatment: color, strokeWidth and curveType still apply to it, but showDots / dotSize do not — an overlay is defined by reading as secondary.

Reference line

referenceLine draws a dashed rule across the value axis — a target, a threshold, or a series average. Pass one config or an array:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop']}
  xKey="month"
  referenceLine={[
    { value: 320, label: 'Target' },
    { average: true, label: 'Average' },
  ]}
/>

A fixed value wins; otherwise average takes the mean of one series (average: 'desktop') or of every plotted series (average: true, which counts any comparisonKeys overlay too) — null values are skipped, and nothing is drawn when there are no numeric values to average. The rule extends the axis domain, so a target above the data maximum stays visible instead of being clipped.

Each caption sits at its rule's top right. Where that lands on the series, labelPosition moves it (top, insideTopLeft, insideTopRight, insideBottomLeft, insideBottomRight):

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop']}
  xKey="month"
  referenceLine={{
    average: true,
    label: 'Average',
    labelPosition: 'insideBottomLeft',
  }}
/>

BarChart and AreaChart take the same config.

Custom tooltip

Replace the tooltip with a configured ChartTooltipContent — imported from the same library, so you never compose recharts yourself. Its formatter renders each row and labelFormatter the header, giving you custom formatting, per-series content, and extra fields:

import { LineChart, ChartTooltipContent } from '@acronis-platform/ui-react';

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  tooltipContent={
    <ChartTooltipContent
      labelFormatter={(label) => `${label} · fiscal Q3`}
      formatter={(value, name) => `${name}: ${value.toLocaleString()}k`}
    />
  }
/>;

Format the tick values with xTickFormatter / yTickFormatter, or hide an axis with showXAxis / showYAxis — the shared axis knobs described under Formatting and hiding axes. Unlike unit, a formatter transforms the value (abbreviate, currency, map to a label); hiding both axes gives a sparkline:

import { LineChart, createTickFormatter } from '@acronis-platform/ui-react';

// Compact currency — 146500 → "$146.5K"
<LineChart
  config={config}
  data={data}
  dataKeys={['desktop']}
  xKey="month"
  yTickFormatter={createTickFormatter({
    style: 'currency',
    currency: 'USD',
    notation: 'compact',
    maximumFractionDigits: 1,
  })}
/>;

Animation

Charts render statically by default. Opt in to an entrance animation with animate, and tune it with animationDuration / animationBegin / animationEasing:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop']}
  xKey="month"
  animate
  animationDuration={800}
  animationEasing="ease-out"
/>

animate honors prefers-reduced-motion: for a visitor who has asked their system to reduce motion, the series render at their final geometry with no animation (the same applies when rendering on the server).

Data labels

Annotate each point with its value using showLabels. labelFormatter takes the same formatters as the axes, so a label and its axis read alike:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop']}
  xKey="month"
  showLabels
  labelFormatter={formatCompactNumber}
/>

labelPosition moves them; the default is the series' growing end (top, or right for horizontal bars). Under layout="stacked" that end is covered by the next segment, so each value centres inside its own segment instead — and switches to a token that stays legible over the fill:

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop']}
  xKey="month"
  showLabels
  labelPosition="insideEnd"
/>

Labels are off by default — with more than one series they collide easily, so turn them on for a single series or a chart with room to spare.

Range brush

Long series don't have to be read all at once: showBrush renders a range selector under the plot. Drag a handle — or the selected window itself — and the series, its category axis, and the tooltip all follow the selection. brushHeight sizes the strip (28px by default):

<LineChart
  config={config}
  data={data}
  dataKeys={['desktop']}
  xKey="month"
  showBrush
/>

The brush indexes rows, so it slices the category axis. It is independent of showXAxis, so a chart with its axis hidden still gets a working brush. The two range captions appear on the strip while you hover, drag, or focus a handle.

Both handles are focusable role="slider" elements: Tab reaches them, and the arrow keys move the selection one row at a time. Pass brushAriaLabel to localize their accessible name (it defaults to 'Chart range selector').

Accessibility

Neither LineChart nor the shared ChartContainer adds a role or an aria-* attribute of its own. What semantics the chart has come from recharts, whose accessibility layer is on by default and is not switched off here: the plot's <svg> renders role="application" with tabindex="0", so Tab reaches it and focusing it opens the tooltip on the first category. The left/right arrow keys step it along the categories. Enter toggles the tooltip rather than pinning it, and since focus has already opened one, the first Enter closes it — press Enter again, or an arrow key, to bring it back.

That surface has no accessible name, though, and the layer to fix that is this one rather than recharts. recharts takes title, desc and role as first-class chart props and writes them straight into the plot's <svg>; LineChart passes none of them down, because it spreads the props it doesn't consume onto its own root <div> and hands the recharts chart only the data and margins it computes itself. The <title> and <desc> recharts always emits therefore stay empty and a screen reader announces an unnamed application region. The tooltip is not a live region either: ChartTooltipContent renders plain markup with no role="status" or aria-live, so the values it reveals as you arrow along are shown but never announced.

So name the chart from the outside: role and aria-label (or aria-labelledby) land on that same root <div>. Choose the role before you copy the snippet, though. role="img" names the chart and also makes its subtree presentational, hiding the plot from assistive technology while it stays in the tab order; that is the right trade when the picture is the whole message. Where the keyboard tooltip should stay reachable, wrap the chart in a <figure> with a <figcaption>, or pass role="group" with the same aria-label, and leave the plot's own semantics alone.

<LineChart
  role="img"
  aria-label="Desktop and mobile sessions, January to March"
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
/>

A name is not a text alternative. The chart is a visual encoding of numbers, and "Sessions by month" says what the picture is about, not what it shows — so put the numbers in text as well: a caption carrying the takeaway, a summary sentence, or the same rows in an adjacent Table. Series are told apart by hue alone unless you say otherwise, so on a multi-series chart reach for lineStyle="dashed", lineSettings[key].dashed, a distinct curveType, or per-series dots to give each one a second cue. comparisonKeys already carries one: an overlay series renders dashed and dimmed, so it reads as secondary without depending on its hue.

The range brush is the one part of the plot with real control semantics. Each traveller is a role="slider" element with tabindex="0", named by brushAriaLabel, and the left/right arrow keys move that end of the selection one row at a time — the traveller consumes those keys, so they resize the range instead of stepping the tooltip. ChartContainer restores the focus outline that its blanket outline-hidden would otherwise take away. Its aria-valuenow is recharts' own: the handle's pixel position, not a row index, so what a screen reader reads back from it carries no meaning.

Direction (RTL)

Under dir="rtl" the chrome around the plot mirrors on its own — the legend, the tooltip, and any readouts you compose alongside the chart are ordinary flow layout built from logical CSS utilities. The plot area itself deliberately stays left-to-right: recharts lays every mark out in physical SVG coordinates, so a mirrored category axis would flip the reading order of the data without flipping the geometry that encodes it. This is what mainstream charting libraries do by default.

ChartContainer pins .recharts-surface to direction: ltr so that holds whatever the page inherits, and the pin is load-bearing rather than a formality: recharts anchors axis tick text with the direction-relative SVG keywords text-anchor: start / end while placing each mark at a coordinate it computed in physical space, so an inherited dir="rtl" mirrors the text away from the geometry — end-anchored Y-axis ticks land inside the plot area, rotated X-axis ticks drift, and a ReferenceLine label at the right edge overflows the surface. Text anchored middle is direction-immune either way: unrotated X-axis ticks, axis titles, and the values showLabels draws. Because the pin stops at the surface, the tooltip and the legend — HTML outside it — still mirror. Chart carries the measured detail.

API Reference

Prop

Type

Edit on GitHub

On this page