Chart
Explore an observation window
Hover over a line, area, or bar plot to compare all visible series at the nearest
sample coordinate. Click or tap to retain the readout. Focus the plot and use arrow
keys to inspect adjacent samples; Home and End jump to the first and last sample.
Escape or Clear inspection clears the crosshair. Escape goes no further when it
clears an inspection, so a dialog around the chart stays open until the next Escape.
Readouts show exact values, their digits grouped in the application's locale, units,
and Missing when a series has no measurement at that coordinate. A sample's authored label names its position when every visible
series labels that sample the same way; otherwise the position uses formatX.
Relay latency
Fictional training snapshot · 30 minutes at 30-second intervals. North relay has a reporting gap and a latency excursion.
Fictional training snapshot · 30 minutes at 30-second intervals. North relay has a reporting gap and a latency excursion. area chart. 2 visible series. 4 missing samples. Use View data to inspect values.
2026-09-29 14:00:00 UTC to 2026-09-29 14:30:00 UTC
Time (UTC)
- North relay
- —
- South relay
- —
Review threshold: 40 ms
4 missing samples shown as gaps.
Drag either overview handle to narrow the observation window. Both handles work with arrow keys, Home, End, Page Up, and Page Down. The overview retains the full dataset, including gaps. Reset range restores the full window.
const [range, setRange] = useState<ChartRange>({ start, end });
<Chart
label="Relay latency"
series={fullHistory}
variant="area"
fill="hatched"
unit="ms"
xUnit="UTC"
formatX={formatUTC}
rangeSelection
xDomain={{ start, end }}
range={range}
onRangeChange={setRange}
/>;
range uses numeric x coordinates, including epoch milliseconds for time series.
Pass the full history and share range state with TimeRange or another view.
Supply xDomain={{ start, end }} for the complete observation period when samples
do not reach its boundaries. Omit range for internal state. Invalid or reversed
bounds fall back to the full domain; overlapping bounds clamp to it. A valid
controlled window outside the data stays empty. The handles move continuously in 0.1%
steps of the full domain, without snapping to observations. A narrow window may
contain no samples; its overview and reset control remain available.
Use fill="hatched" for textured area or bar fills. Series outlines and marker
shapes preserve identity. Hover or focus a visible legend entry to emphasize its
series; activate it to toggle visibility. Emphasis fades respect reduced motion;
inspection and range updates are immediate.
Basic
Shared review marker
Supply cursor and an optional cursorLabel to mark a shared review position in line, area or bar charts. The solid marker follows the supplied x coordinate between samples without creating a measurement there. Its visible text uses formatX. Local pointer and keyboard sample inspection remains independent. Invalid or out-of-window cursors are omitted.
<Chart
label="Pressure history"
series={history}
unit="bar"
cursor={reviewTime}
cursorLabel="Review time"
formatX={formatUTC}
/>
Supply explicit measurements and units. Lines connect adjacent finite samples in
authored order. Null, NaN, or infinite coordinates break the path; zero remains zero.
Link latency
Synthetic samples from two relay links. The north link has one missing observation.
Synthetic samples from two relay links. The north link has one missing observation. line chart. 2 visible series. 1 missing samples. Use View data to inspect values.
0 s to 70 s
Elapsed (s)
- North
- —
- South
- —
Review threshold: 30 ms
1 missing samples shown as gaps.
<Chart
label="Link latency"
unit="ms"
xLabel="Elapsed"
xUnit="s"
series={[
{
id: "north",
label: "North",
data: [
{ x: 0, y: 24 },
{ x: 10, y: null },
{ x: 20, y: 22 },
],
},
]}
threshold={{ value: 30, label: "Review threshold" }}
/>
Variants
area · Link latency
Series visibility is controlled and persists when the chart variant changes.
Series visibility is controlled and persists when the chart variant changes. area chart. 2 visible series. 1 missing samples. Use View data to inspect values.
0 s to 70 s
Sample (s)
- North
- —
- South
- —
1 missing samples shown as gaps.
| Variant | Interpretation |
|---|---|
line | Connected samples with gaps for missing data. |
area | Line filled to zero, including negative values. |
bar | Grouped values at numeric x coordinates; zero baseline. |
scatter | Unconnected observations. |
histogram | Counts of raw y values in shared bins. |
heatmap | Numbered series rows, equally spaced sample columns; lightness encodes y, dashes mark missing cells. The legend shows each row's number beside its full name. |
Use unique, stable series IDs. Order line/area samples by ascending x; use unique
x coordinates per series for bars and heatmaps. Heatmap columns follow numeric x
order, but width does not encode elapsed time.
Histogram bins and data tables
Histograms exclude and count missing samples. bins defaults to 10, clamps to
1–100, and becomes one for constant data. The final interval includes its maximum.
The table contains bin intervals and counts for visible series. Other variants
show all authored samples, including hidden series, exact numbers, and point labels.
Numbers keep every supplied digit and group them in the application's locale, as the
readout does, so a value reads the same in the table, the readout and the scale.
Bin intervals separate their ends with a comma, or with a semicolon where the decimal
mark is a comma: [12.3, 28.55) in English, [12,3; 28,55) in German.
Numbers
A chart writes every number one way: the axes, the readout, the range line, the scale, the
table and the counts in its sentences group digits in the application's locale (React Aria's
I18nProvider), so 12,500 never appears as 12500 in the same chart. A number that is
not a quantity, such as a year, a sequence number or an ID, is yours to format: pass
formatX for the x axis.
<Chart
label="Pumps commissioned"
variant="bar"
formatX={String}
series={[
{
id: "pumps",
label: "Pumps",
data: [
{ x: 2024, y: 3 },
{ x: 2025, y: 5 },
{ x: 2026, y: 2 },
],
},
]}
/>
Without formatX the years read as quantities: 2,024 to 2,026.
The y axis keeps its labels plain and grouped up to 11 characters, such as 10,000,000 or
-2,000,000; the left margin widens to hold them. A longer label, or a spacing too fine to
write in full, switches the axis to scientific notation (1.2338E7), and a flat series
whose one value is too long does the same. No label is cut, and the exact value stays in the
tick's title, the readout and the table. Tick labels on the x axis show the exact value too;
only an interpolated middle tick drops the rounding noise of its arithmetic, so 0.01 to
0.23 reads 0.01 · 0.12 · 0.23.
A heatmap cell shows a compact value, to four significant digits (98.61, 1,235), and
only when it fits. Its title and the table keep the exact value.
Controlled visibility
Control visibility with hiddenSeries / onHiddenSeriesChange, or omit hiddenSeries
for internal state. Stable IDs determine color, marker, and dash pattern across reorders.
Set color to 1–8 to choose a categorical token; colors do not encode severity.
const [hidden, setHidden] = useState<string[]>([]);
<Chart
label="Link latency"
series={series}
unit="ms"
hiddenSeries={hidden}
onHiddenSeriesChange={setHidden}
/>;
Scales and thresholds
Scales follow visible data with round-number intervals. Bar, area, and histogram scales include zero; histogram counts use whole-number ticks. Thresholds expand the y domain and use a labeled dashed line. Histogram thresholds refer to counts; heatmaps ignore thresholds.
Disabled
disabled locks legend and range changes; data inspection stays available.
Archived observation
Series controls are disabled; the original samples remain available through View data.
Series controls are disabled; the original samples remain available through View data. line chart. 2 visible series. 1 missing samples. Use View data to inspect values.
0 s to 70 s
Sample (s)
- North
- —
- South
- —
1 missing samples shown as gaps.
Props
| PROP | TYPE | DEFAULT | REQUIRED |
|---|---|---|---|
labelVisible chart heading and accessible graphic name. | string | — | yes |
seriesReadonly ChartSeries[] with unique stable IDs. Points are { x: number, y: number | null, label?: string }; null and non-finite coordinates are missing. Line/area preserve authored order; use ascending x. Heatmap requires unique x within each series. | array | — | yes |
variant | enumline | area | bar | scatter | histogram | heatmap | "line" | no |
fillArea and bar fill treatment. Hatching adds texture while outlines carry series color and measurement. | enumsolid | hatched | "solid" | no |
descriptionVisible supporting context, also included in the graphic description. | string | — | no |
xLabelX axis label. Defaults to Sample, or Value for histograms. | string | — | no |
xUnitX axis unit. Histogram x values use unit instead. | string | — | no |
yLabelY axis label. Defaults to Value, or Count for histograms. | string | — | no |
unitMeasured value unit; shown on axes, thresholds, and the heatmap scale. | string | — | no |
formatX(finiteX: number) => string. Formats x tick labels, the full start and end of the window in the line under the plot, and sample positions when no authored point label exists. For a time axis, return YYYY-MM-DD HH:MM:SS and set xUnit="UTC". Histogram bin bounds remain numeric. | function | — | no |
hiddenSeriesControlled hidden series IDs. Omit for internal visibility state. Hidden series are excluded from scales; raw data inspection still includes them. | array | — | no |
onHiddenSeriesChange(ids: string[]) => void. Reports the requested hidden IDs; supplied hiddenSeries remains controlled. | function | — | no |
disabledDisables legend and range changes. Pointer/keyboard inspection and View data remain available. | boolean | false | no |
rangeSelectionShows a miniature overview with draggable and keyboard-operable start/end handles for line, area, and bar. Needs a non-zero x domain and at least one finite sample coordinate. Other variants ignore it. | boolean | false | no |
xDomainOptional ChartRange { start, end } defining the full observation bounds for line/area/bar, including periods without measurements. Defaults to the extent of supplied x values. Pass snapshot bounds when linking time controls. | node | — | no |
rangeControlled ChartRange { start: number, end: number } in x units. Filters line/area/bar samples inclusively. Overlapping bounds clamp to the full domain; invalid or reversed bounds use it in full. Valid disjoint controlled ranges stay empty. Omit for internal range state. Supply full history for the overview. | node | — | no |
onRangeChange(range: ChartRange) => void. Reports requested x bounds from range handles or reset. Supplied range remains controlled. Keyboard steps span 0.1% of the full domain; handles do not snap to observations. | function | — | no |
cursorOptional supplied x position for line/area/bar. Draws an independent review marker without selecting or interpolating a measurement. Invalid/out-of-window positions are omitted; null clears it. | number | — | no |
cursorLabelVisible label for the supplied marker. Its value uses formatX and xUnit. | string | "Review position" | no |
binsHistogram bin count, floored and clamped to 1–100. Non-finite values use 10. Constant data uses one bin. Each histogram bins valid point.y values on a shared range; the last bin includes the maximum. | number | 10 | no |
thresholdOptional { value: number, label: string } on the measured y axis (count for histograms). Adds a dashed line and text. Non-finite values and heatmap thresholds are ignored. | node | — | no |
Accessibility
- Labeled graphic with explicit empty, missing, and all-hidden states.
- View data opens a captioned table in a keyboard-focusable scroll region.
- Enter/Space toggles series. Buttons expose
aria-pressed, a Hidden indicator, and visible focus. - Marker shapes and dash patterns distinguish series alongside color. Tables preserve exact identities and values when marks overlap or colors repeat.
- Heatmap values appear in cells to four significant digits when they fit; the exact value is in each cell's title, and the table is always available. In forced-colors mode the cells, their values and the scale keep the sequential palette, which is the data.
- The title is a heading, like the titles of the other panels.
- Line, area, and bar inspection adds one keyboard stop. Its spoken value includes the sample coordinate, visible series names, exact values, and missing data. Clear inspection stays focusable when there is nothing to clear and is announced as unavailable, so clearing never moves keyboard focus.
- Range handles have distinct start/end labels and announce formatted x values.
- View data retains all supplied samples when the plot is narrowed.
Responsive layout and dataset limits
Geometry follows container width; height and 11px labels stay fixed. Narrow charts
reduce ticks and shorten long tick labels; a line under the plot always gives the full
start and end of the window as text, using formatX and xUnit (for a time axis,
format as YYYY-MM-DD HH:MM:SS with xUnit="UTC"). Y ticks keep the digits that tell
neighboring gridlines apart; x ticks and heatmap columns show their exact positions.
Heatmap scales wrap; cell values keep their full text size.
At high density, fewer markers are drawn, but every finite point stays in its line and
in the table. Line and area charts draw no element per point: the crosshair readout and
keyboard inspection give each sample's values, so hovering a point shows no browser
tooltip. Scatter marks, bars and heatmap cells keep their hover titles. Segment
boundaries and the last finite point remain marked. Keep datasets bounded: Chart has no virtualization or automatic downsampling.
Line, area, and bar support an observation window through rangeSelection; the
other variants do not zoom. Chart does not fetch, interpolate, or downsample data.
Manifest entry
<Chart label="Link latency" unit="ms" xLabel="Elapsed" xUnit="s" series={[{ id: "alpha", label: "Alpha", data: [{ x: 0, y: 22 }, { x: 1, y: null }, { x: 2, y: 25 }] }]} />View agent manifest
{
"description": "Native SVG charts with stable series identity and exact data tables. Line, area, and bar support shared pointer/keyboard readouts and an optional controlled observation window.",
"props": {
"label": {
"type": "string",
"required": true,
"description": "Visible chart heading and accessible graphic name."
},
"series": {
"type": "array",
"items": {
"id": "string (unique, stable)",
"label": "string",
"data": "readonly ChartPoint[]",
"color": "1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 (optional categorical token)"
},
"required": true,
"description": "Readonly ChartSeries[] with unique stable IDs. Points are { x: number, y: number | null, label?: string }; null and non-finite coordinates are missing. Line/area preserve authored order; use ascending x. Heatmap requires unique x within each series."
},
"variant": {
"type": "enum",
"values": [
"line",
"area",
"bar",
"scatter",
"histogram",
"heatmap"
],
"default": "line"
},
"fill": {
"type": "enum",
"values": [
"solid",
"hatched"
],
"default": "solid",
"description": "Area and bar fill treatment. Hatching adds texture while outlines carry series color and measurement."
},
"description": {
"type": "string",
"description": "Visible supporting context, also included in the graphic description."
},
"xLabel": {
"type": "string",
"description": "X axis label. Defaults to Sample, or Value for histograms."
},
"xUnit": {
"type": "string",
"description": "X axis unit. Histogram x values use unit instead."
},
"yLabel": {
"type": "string",
"description": "Y axis label. Defaults to Value, or Count for histograms."
},
"unit": {
"type": "string",
"description": "Measured value unit; shown on axes, thresholds, and the heatmap scale."
},
"formatX": {
"type": "function",
"description": "(finiteX: number) => string. Formats x tick labels, the full start and end of the window in the line under the plot, and sample positions when no authored point label exists. For a time axis, return YYYY-MM-DD HH:MM:SS and set xUnit=\"UTC\". Histogram bin bounds remain numeric."
},
"hiddenSeries": {
"type": "array",
"description": "Controlled hidden series IDs. Omit for internal visibility state. Hidden series are excluded from scales; raw data inspection still includes them."
},
"onHiddenSeriesChange": {
"type": "function",
"description": "(ids: string[]) => void. Reports the requested hidden IDs; supplied hiddenSeries remains controlled."
},
"disabled": {
"type": "boolean",
"default": false,
"description": "Disables legend and range changes. Pointer/keyboard inspection and View data remain available."
},
"rangeSelection": {
"type": "boolean",
"default": false,
"description": "Shows a miniature overview with draggable and keyboard-operable start/end handles for line, area, and bar. Needs a non-zero x domain and at least one finite sample coordinate. Other variants ignore it."
},
"xDomain": {
"type": "node",
"description": "Optional ChartRange { start, end } defining the full observation bounds for line/area/bar, including periods without measurements. Defaults to the extent of supplied x values. Pass snapshot bounds when linking time controls."
},
"range": {
"type": "node",
"description": "Controlled ChartRange { start: number, end: number } in x units. Filters line/area/bar samples inclusively. Overlapping bounds clamp to the full domain; invalid or reversed bounds use it in full. Valid disjoint controlled ranges stay empty. Omit for internal range state. Supply full history for the overview."
},
"onRangeChange": {
"type": "function",
"description": "(range: ChartRange) => void. Reports requested x bounds from range handles or reset. Supplied range remains controlled. Keyboard steps span 0.1% of the full domain; handles do not snap to observations."
},
"cursor": {
"type": "number",
"description": "Optional supplied x position for line/area/bar. Draws an independent review marker without selecting or interpolating a measurement. Invalid/out-of-window positions are omitted; null clears it."
},
"cursorLabel": {
"type": "string",
"default": "Review position",
"description": "Visible label for the supplied marker. Its value uses formatX and xUnit."
},
"bins": {
"type": "number",
"default": 10,
"description": "Histogram bin count, floored and clamped to 1–100. Non-finite values use 10. Constant data uses one bin. Each histogram bins valid point.y values on a shared range; the last bin includes the maximum."
},
"threshold": {
"type": "node",
"description": "Optional { value: number, label: string } on the measured y axis (count for histograms). Adds a dashed line and text. Non-finite values and heatmap thresholds are ignored."
}
},
"composable_with": [
"Card",
"DataGrid",
"TimeRange",
"SplitPane"
],
"never_inside": [
"Chart",
"Button"
],
"example": "<Chart label=\"Link latency\" unit=\"ms\" xLabel=\"Elapsed\" xUnit=\"s\" series={[{ id: \"alpha\", label: \"Alpha\", data: [{ x: 0, y: 22 }, { x: 1, y: null }, { x: 2, y: 25 }] }]} />"
}