ScatterChart
A typed scatter / bubble chart for correlation across two or three numeric fields.
Usage
import { ScatterChart } from '@acronis-platform/ui-react';
import type { ChartConfig } from '@acronis-platform/ui-react';ScatterChart is a typed composition over the shared Chart
primitives. Give it a series list (each { key, data } with its own points), a
per-series config, and the numeric axis fields (xKey / yKey) — it renders a
themed recharts scatter chart (tooltip, legend, axes, grid included), so you
don't hand-compose recharts children. Add a zKey to size points by a third
field (a bubble chart).
Each series carries its own point array (points aren't columns of a shared
row the way bar/line/area series are), so the API takes a series list rather
than dataKeys.
Series colors come from the palette prop, not from config — see
Palettes. Each config entry, keyed by a
series[].key, maps that series to a label and an optional tone that
re-points it within the palette. ScatterChart resolves them into the
--color-<key> custom properties its points fill from.
Examples
Two groups plotted on shared x/y axes, with a themed tooltip and legend:
const series = [
{ key: 'classA', data: [{ hours: 2, score: 55 }, { hours: 6, score: 78 }] },
{ key: 'classB', data: [{ hours: 1, score: 70 }, { hours: 9, score: 95 }] },
];
const config = {
classA: { label: 'Class A' },
classB: { label: 'Class B' },
} satisfies ChartConfig;
<ScatterChart
config={config}
series={series}
xKey="hours"
yKey="score"
className="h-[360px] w-[520px]"
/>;Turn it into a bubble chart by mapping a third numeric field to point size with
zKey:
<ScatterChart config={config} series={series} xKey="hours" yKey="score" zKey="weight" zRange={[60, 500]} />Change the marker with shape, and toggle the chrome with showGrid /
showTooltip / showLegend:
<ScatterChart config={config} series={series} xKey="hours" yKey="score" shape="triangle" showLegend={false} />Add axis titles and unit suffixes with xAxisLabel / yAxisLabel / xUnit /
yUnit — both axes are numeric here, so both units apply. The titles inherit
the theme token:
<ScatterChart
config={config}
series={series}
xKey="hours"
yKey="score"
xAxisLabel="Hours studied"
yAxisLabel="Score"
yUnit="%"
/>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 { ScatterChart, ChartTooltipContent } from '@acronis-platform/ui-react';
<ScatterChart
config={config}
series={series}
xKey="hours"
yKey="score"
tooltipContent={
<ChartTooltipContent
hideLabel
formatter={(value, name) => `${name}: ${value.toLocaleString()}`}
/>
}
/>;Format the tick values with xTickFormatter / yTickFormatter, or hide an axis
with showXAxis / showYAxis — the shared axis knobs described under
Formatting and hiding axes. A
formatter can map numbers to labels, which the unit suffix can't:
import { ScatterChart } from '@acronis-platform/ui-react';
<ScatterChart
config={config}
series={series}
xKey="hours"
yKey="score"
yTickFormatter={(score) =>
Number(score) >= 80 ? 'High' : Number(score) >= 60 ? 'Mid' : 'Low'
}
/>;Animation
Charts render statically by default. Opt in to an entrance animation with
animate, and tune it with animationDuration / animationBegin /
animationEasing:
<ScatterChart config={config} data={data} 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).
Accessibility
Neither ScatterChart 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 point. The left/right arrow
keys step it from point to point. 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>;
ScatterChart 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 coordinates 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.
<ScatterChart
role="img"
aria-label="Exam score against hours studied, by class"
config={config}
series={series}
xKey="hours"
yKey="score"
/>A name is not a text alternative. A scatter exists to show a relationship — the
correlation, the cluster, the outlier — and that reading is nowhere in the label,
so state it: a caption with the takeaway, a summary sentence, or the points in an
adjacent Table. Note that shape sets the marker for
every point rather than per series, so on a grouped scatter hue is the only
thing separating one series from another; when that grouping is part of the
finding, make sure it survives into the text alternative. The same goes for a
bubble chart: zKey maps a third field to point area, which nothing announces.
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 x-axis would flip the reading order of the data without flipping the
geometry that encodes it — and on a scatter that geometry is the finding, since
the slope of a cloud of points would reverse with 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, and
rotated X-axis ticks drift. Text anchored middle is direction-immune either
way: unrotated X-axis ticks and axis titles. 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