Acronis UIKit
Components

BarChart

A typed bar chart — grouped or stacked — plus a labelled proportional bar list.

Usage

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

BarChart is two renderings behind one name, selected by orientation.

The default, orientation="vertical", 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 bar chart (tooltip, legend, axes, grid included), so you don't hand-compose recharts children.

orientation="horizontal" renders something else entirely: a labelled proportional bar list, one row per items entry, with no axes or grid. See Horizontal. Everything on this page other than that section describes the default chart.

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. BarChart resolves them into the --color-<key> custom properties its bars fill from.

Examples

A vertical, grouped bar 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;

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

Stack the series by setting layout="stacked":

<BarChart config={config} data={data} dataKeys={['desktop', 'mobile']} xKey="month" layout="stacked" />

Toggle the chrome and corner rounding with showGrid / showTooltip / showLegend / barRadius:

<BarChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  showLegend={false}
  barRadius={0}
/>

Legend entries follow the order you declare in dataKeys, so the legend reads left to right in the same order as the bars in a group.

Overlay a dashed reference line on the value axis with referenceLine — either a fixed target value, or a computed average (the mean of one series by key, or of every plotted series when true). An optional label captions it. Pass an array to draw several at once:

// Fixed target
<BarChart config={config} data={data} dataKeys={['desktop']} xKey="month"
  referenceLine={{ value: 250, label: 'Target' }} />

// Mean of the plotted series
<BarChart config={config} data={data} dataKeys={['desktop']} xKey="month"
  referenceLine={{ average: true, label: 'Average' }} />

// Several lines at once
<BarChart config={config} data={data} dataKeys={['desktop']} xKey="month"
  referenceLine={[{ value: 300, label: 'Target' }, { average: true, label: 'Average' }]} />

Add axis titles and a unit suffix with xAxisLabel / yAxisLabel / yUnit (the y-axis is the numeric one). The titles inherit the theme token, so they stay legible in both modes:

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

Bar styling

barShape changes how every bar is painted — rounded (the default, the growing end rounded by barRadius), pill, gradient, or pattern (diagonal hatching in the series color, which reads without relying on hue):

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

Size the bars with barSize / maxBarSize and the spacing with barGap (between the bars of one category) / barCategoryGap (between categories). Defaults: barSize={8}, barGap={4}, barRadius={8}. minPointSize keeps a near-zero value visible as a sliver, and showBackground draws a full-height track behind every bar (backgroundFill recolors it):

<BarChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  barSize={16}
  barGap={2}
  barCategoryGap="25%"
  minPointSize={4}
  showBackground
/>

showActiveBar highlights the bar under the pointer; activeBar sets its fill and opacity (it keeps the series color by default).

Highlighting a range

referenceArea shades a range of categories — a forecast period, a quarter under review. from/to are inclusive and take either the category's own value or its 0-based row index, and an omitted bound runs to that end of the data. The category ticks under the band pick up the accent styling unless you pass highlightTicks: false, and divider marks the hand-off with a dashed rule on the band's leading edge:

<BarChart config={config} data={data} dataKeys={['desktop']} xKey="month"
  referenceArea={{ from: 'Apr', label: 'Forecast', divider: true }} />

barSettings restyles the bars of one series over the same kind of range, keyed by dataKeys entry. Combine the two to make a projection read as provisional — translucent and dashed, over its own track, inside the band:

<BarChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  referenceArea={{ from: 'Apr', label: 'Forecast' }}
  barSettings={{
    desktop: { from: 'Apr', opacity: 0.35, dashed: true, background: true },
    mobile: { from: 'Apr', opacity: 0.35, dashed: true, background: true },
  }}
/>

Each entry takes fill, opacity, dashed, shape (any barShape value) and background. Bars outside the range — and series with no entry — render normally, so a grouped or stacked chart keeps its usual painting everywhere else.

background is either true for a full-height track, or the name of a data field to cap it at that row's value — the headroom between a projection and its upper bound. A capped track stacks on its own bar, so it needs the default layout="grouped", and it stays out of the tooltip and the legend:

const data = [
  // …actuals…
  { month: 'Aug', new: 12.6, newMax: 14.6 },
  { month: 'Sep', new: 13.3, newMax: 16 },
];

<BarChart config={config} data={data} dataKeys={['new']} xKey="month"
  referenceArea={{ from: 'Aug' }}
  barSettings={{ new: { from: 'Aug', opacity: 0.35, dashed: true, background: 'newMax' } }} />

A capped track is decoration for the bar under it, so it never becomes a tooltip row or a legend entry. Pair the range with a tooltipContent when the projection should read as an estimate there too:

<BarChart
  config={config}
  data={data}
  dataKeys={['new']}
  xKey="month"
  referenceArea={{ from: 'Aug', divider: true }}
  barSettings={{ new: { from: 'Aug', opacity: 0.35, dashed: true, background: 'newMax' } }}
  tooltipContent={
    <ChartTooltipContent
      labelFormatter={(label) => (isForecast(label) ? `${label} · forecast` : label)}
    />
  }
/>

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 { BarChart, ChartTooltipContent } from '@acronis-platform/ui-react';

<BarChart
  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):

import { BarChart, formatCompactNumber } from '@acronis-platform/ui-react';

<BarChart
  config={config}
  data={data}
  dataKeys={['desktop', 'mobile']}
  xKey="month"
  yTickFormatter={formatCompactNumber}
/>;

Animation

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

<BarChart 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:

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

labelPosition moves them; the default is the series' growing end (top). 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:

<BarChart 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):

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

The brush indexes rows, so it slices the category (x) axis. It is independent of showXAxis, so a chart with its category 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 BarChart 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>; BarChart 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.

<BarChart
  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 barShape="pattern" (diagonal hatching that reads without relying on hue), barSettings[key].dashed, or showLabels to give each one a second cue.

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. orientation="horizontal" is not part of this: it renders no SVG at all, so its rows are ordinary flow layout and mirror with the page.

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.

Horizontal

Pass orientation="horizontal" to render labelled proportional bars instead of a chart — one row per items entry showing its label, its value, its share of max, and a track filled to that share. There are no axes, no grid, and no recharts: each row is Base UI's Meter primitive, so it carries role="meter" and its numbers reach the accessibility tree.

import { BarChart } from '@acronis-platform/ui-react';

<BarChart
  orientation="horizontal"
  palette={{ type: 'status' }}
  items={[
    { label: 'Critical', value: 6, tone: { status: 'danger' } },
    { label: 'High', value: 9, tone: { status: 'warning' } },
  ]}
  max={29}
/>;

Each item takes either a color (any CSS color string) or a tone together with a palette. The palette prop works the same way as in the vertical chart — { type: 'status' } maps each tone.status value ('danger', 'warning', 'info', 'success', 'neutral') to its corresponding status color. When both are provided, color takes precedence.

max defaults to the sum of all items[].value when omitted — which gives each row its own share of the whole. Pass an explicit max when the rows are shares of a total they don't add up to.

valueFormatter formats the number next to each label (toLocaleString() by default). showTooltip toggles the per-row hover card — a row is focusable only while it is on, so the tooltip stays reachable from the keyboard — and tooltip replaces its content:

<BarChart
  orientation="horizontal"
  palette={{ type: 'status' }}
  items={items}
  max={29}
  valueFormatter={(value) => `${value} alerts`}
  showTooltip={false}
/>;

Forecast

Add a forecast value to any item to render a translucent projection bar extending beyond the actual value. The actual bar renders solid on top; text and percentages always reflect the actual value:

<BarChart
  orientation="horizontal"
  palette={{ type: 'status' }}
  items={[
    { label: 'Critical', value: 6, tone: { status: 'danger' }, forecast: 10 },
    { label: 'High', value: 9, tone: { status: 'warning' }, forecast: 12 },
  ]}
  max={29}
/>;

The forecast bar is the same color at 30% opacity; aria-valuenow reflects the actual value and aria-valuetext also mentions the forecast. forecast is ignored when <= value.

None of the chart props above apply here: data / config / dataKeys / xKey, the axis and grid knobs, layout, barShape, referenceLine, referenceArea, barSettings and the brush are all orientation="vertical" only.

API Reference

BarChartVerticalProps

Props when orientation is omitted or "vertical" — the recharts bar chart with axes, grid, tooltip and legend.

Prop

Type

BarChartHorizontalProps

Props when orientation="horizontal" — the labelled proportional bar list built on Base UI's Meter primitive.

Prop

Type

BarChartItem

Each entry in the items array for the horizontal mode.

Prop

Type

Edit on GitHub

On this page