Scatter Chart
Correlation between a numeric X field and one or more Y series, plotted as colored dots.
Overview
ScatterChart is the styled wrapper over recharts' ScatterChart, composed on the shadcn chart primitive (ChartContainer). xKey is a numeric field (linear X axis); each entry in config is a numeric Y series. The chart wires up the cartesian grid, the numeric X and Y axes, an optional tooltip, and one set of dots per series.
Reach for ScatterChart when you need to surface correlation, density, or outliers - response time vs payload size, conversion rate vs traffic, runtime vs input. For ordered category trends use LineChart or AreaChart; for discrete category comparison use BarChart.
Preview
Installation
bash npx gremorie@latest add rx-scatter-chart bash pnpm dlx gremorie@latest add rx-scatter-chart
bash yarn dlx gremorie@latest add rx-scatter-chart
bash bunx --bun gremorie@latest add rx-scatter-chart
Usage
import { ScatterChart } from "@gremorie/rx-data";
import type { ChartConfig, ChartDatum } from "@gremorie/rx-data";
const data: ChartDatum[] = [
{ x: 30, y: 100 },
{ x: 50, y: 200 },
{ x: 70, y: 150 },
{ x: 90, y: 250 },
{ x: 110, y: 175 },
{ x: 130, y: 300 },
];
const config: ChartConfig = {
y: { label: "Value", color: "var(--chart-1)" },
};
export function CorrelationChart() {
return <ScatterChart data={data} config={config} xKey="x" />;
}Angular edition planned for a follow-up release; it mirrors this component's anatomy.
API
<ScatterChart>
The styled wrapper over recharts' ScatterChart, composed on ChartContainer. Renders the responsive frame, a cartesian grid, the numeric X and Y axes, an optional tooltip, and one <Scatter> per config key colored var(--color-<key>).
| Prop | Type | Default | Description |
|---|---|---|---|
data | ChartDatum[] | - | Tabular data. Each row carries the numeric xKey value plus one numeric value per config key. |
config | ChartConfig | - | Maps each Y series key to { label, color }. The color is any CSS color or token, typically "var(--chart-1)" through "var(--chart-5)". ChartContainer injects each as a var(--color-<key>) CSS variable. |
xKey | string | - | Name of the numeric field on each row used as the X axis. Linear, not categorical. |
tooltip | boolean | true | Toggles the hover tooltip. |
className | string | - | Merged onto the ChartContainer. |
Composing with the chart primitive
For full control, compose recharts directly inside ChartContainer using the exported primitives. shadcn ships no scatter block, but the composition follows the same pattern as the other shadcn chart blocks.
| Export | Role |
|---|---|
ChartContainer | Responsive frame; injects per-series --color-<key> vars from config. |
ChartTooltip | recharts Tooltip re-export; pair with ChartTooltipContent. |
ChartTooltipContent | Styled tooltip body. |
ChartLegend | recharts Legend re-export; pair with ChartLegendContent. |
ChartLegendContent | Styled legend body. |
useChart | Hook to read the active config inside a custom child. |
Types
| Type | Shape |
|---|---|
ChartDatum | Record<string, string | number> & { fill?: string } - one row of chart data; the optional fill sets a per-point color. |
ChartConfig | Record<string, { label?: ReactNode; color?: string }> - per-series label and color map. |
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
type ChartDatum,
} from '@gremorie/rx-data';
import { CartesianGrid, Scatter, ScatterChart, XAxis, YAxis } from 'recharts';
const config = {
y: { label: 'Value', color: 'var(--chart-1)' },
} satisfies ChartConfig;
export function Example({ data }: { data: ChartDatum[] }) {
return (
<ChartContainer config={config}>
<ScatterChart
accessibilityLayer
margin={{ left: 12, right: 12, top: 8, bottom: 8 }}
>
<CartesianGrid />
<XAxis
dataKey="x"
type="number"
name="x"
tickLine={false}
axisLine={false}
tickMargin={8}
/>
<YAxis
type="number"
tickLine={false}
axisLine={false}
tickMargin={8}
width={40}
/>
<ChartTooltip
cursor={{ strokeDasharray: '3 3' }}
content={<ChartTooltipContent />}
/>
<Scatter name="y" dataKey="y" fill="var(--color-y)" />
</ScatterChart>
</ChartContainer>
);
}Drop down to this level for bubble charts (size encoded by a third field), per-point coloring via the row's fill, jitter overlays, or a custom legend via ChartLegend + ChartLegendContent.
Composition
xKeymust be numeric. Both axes use recharts'type="number", so the X scale is linear, not a band scale.- Each
configentry becomes one<Scatter>set of dots, colored by itsvar(--color-<key>)variable. The shared Y domain auto-includes every series. - Per-point color is possible by setting a
fillfield on individual rows ofChartDatum; recharts reads it over the series color. - Keep the series count low - two or three before the dots get too dense to read against each other.
The chart auto-sizes to its parent through ChartContainer's responsive frame.
Variations
Single series
<ScatterChart
data={data}
config={{ y: { label: 'Value', color: 'var(--chart-1)' } }}
xKey="x"
/>The default shape. Reach for a single series when surfacing correlation or density across one numeric axis pair.
Multi-series
const config: ChartConfig = {
height: { label: 'Median', color: 'var(--chart-1)' },
max: { label: 'Max', color: 'var(--chart-2)' },
};
<ScatterChart data={data} config={config} xKey="weight" />;For comparing two distributions against the same X axis. Color tokens carry the series distinction; keep the count at two or three before the dots get too dense to read.
Accessibility
- recharts'
accessibilityLayeradds keyboard navigation and screen-reader announcements for the plotted series. --chart-1through--chart-5ship with WCAG AA contrast against the card background in both light and dark themes.- Density caveat: dense scatter plots can hide outliers behind clusters. Set a lower
fillOpacityon a custom<Scatter>or jitter slightly to keep individual points legible.