Skip to content
design
Components

Scatter chart

Inspect independent paired measurements with numeric spacing, source details and caller-formatted values.

On this page

Fuel consumption and RPM

Illustrative paired measurements

  • Port engine
  • Starboard engine
Inspect independent paired measurements with numeric spacing, source details and caller-formatted values.
Code
import type { ReactElement } from "react";
import { ScatterChart } from "@bosunkit/components/scatter-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
// Fixed illustrative display strings; consumers supply their quantity formatter.
export const description: string =
  "Illustrative engine observations retain repeated RPM values and separate paired samples.";
const rpmLabels: Readonly<Record<number, string>> = {
  "1000": "1000 RPM",
  "1200": "1200 RPM",
  "1250": "1250 RPM",
  "1500": "1500 RPM",
  "1750": "1750 RPM",
  "1800": "1800 RPM",
  "2000": "2000 RPM",
  "2200": "2200 RPM",
  "2250": "2250 RPM",
  "2500": "2500 RPM",
};
const fuelLabels: Readonly<Record<number, string>> = {
  "0": "0 L/hr",
  "3.2": "3.2 L/hr",
  "3.8": "3.8 L/hr",
  "4": "4 L/hr",
  "7.1": "7.1 L/hr",
  "7.6": "7.6 L/hr",
  "7.8": "7.8 L/hr",
  "8": "8 L/hr",
  "11.2": "11.2 L/hr",
  "12": "12 L/hr",
  "12.4": "12.4 L/hr",
  "16": "16 L/hr",
};
const formatRPM: (value: string | number) => string = (value: string | number): string =>
  rpmLabels[Number(value)] ?? "";
const formatFuel: (value: number) => string = (value: number): string => fuelLabels[value] ?? "";
const config = {
  port: { label: "Port engine", formatValue: formatFuel },
  starboard: { label: "Starboard engine", formatValue: formatFuel },
};
export default function EngineObservations(): ReactElement {
  return (
    <ChartPanel
      title="Fuel consumption and RPM"
      description="Illustrative paired measurements"
      legend={<ChartLegend config={config} label="Engine observations" mark="point" />}
    >
      <ScatterChart
        data={[
          { rpm: 1800, port: 7.1, starboard: 7.8 },
          { rpm: 1200, port: 3.2, starboard: 3.8 },
          { rpm: 1800, port: 7.6, starboard: null },
          { rpm: 2200, port: 11.2, starboard: 12.4 },
        ]}
        categoryKey="rpm"
        categoryDomain={[1000, 2500]}
        valueDomain={[0, 16]}
        config={config}
        series={["port", "starboard"]}
        formatCategory={formatRPM}
        formatAxisValue={formatFuel}
        label="Illustrative fuel consumption versus RPM"
        categoryLabel="RPM"
        dataTableLabel="View paired observations"
        missingValueText="Unavailable"
        describeObservation={(): string => "Same observation time · illustrative"}
      />
    </ChartPanel>
  );
}

Use scatter plots for engine calibration, RPM versus fuel rate and actual versus expected values. Repeated and unordered x values remain separate observations. Each point must represent a defensible pair: the server owns observation-time matching, source identity and pairing tolerance. Missing measurements have no drawn point and remain explicit in the source table. Different series use different point shapes. A controlled numeric domain clips the drawing while retaining source values.

For a third quantity encoded by circle area, see the cost-versus-latency composition. It composes Recharts through ChartContainer and exposes request volume alongside both axes in a source table.

01Preserve meaningObservation spacing, validity and comparison basis remain explicit.
02Caller display textLabels and formatted values come from the consumer.
03Inspect without hoverNamed controls and source values remain accessible with a keyboard.

Examples

Composition

Fuel consumption and RPM

Illustrative paired measurements

  • Port engine
  • Starboard engine
Illustrative engine observations retain repeated RPM values and separate paired samples.
Code
import type { ReactElement } from "react";
import { ScatterChart } from "@bosunkit/components/scatter-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
// Fixed illustrative display strings; consumers supply their quantity formatter.
export const description: string =
  "Illustrative engine observations retain repeated RPM values and separate paired samples.";
const rpmLabels: Readonly<Record<number, string>> = {
  "1000": "1000 RPM",
  "1200": "1200 RPM",
  "1250": "1250 RPM",
  "1500": "1500 RPM",
  "1750": "1750 RPM",
  "1800": "1800 RPM",
  "2000": "2000 RPM",
  "2200": "2200 RPM",
  "2250": "2250 RPM",
  "2500": "2500 RPM",
};
const fuelLabels: Readonly<Record<number, string>> = {
  "0": "0 L/hr",
  "3.2": "3.2 L/hr",
  "3.8": "3.8 L/hr",
  "4": "4 L/hr",
  "7.1": "7.1 L/hr",
  "7.6": "7.6 L/hr",
  "7.8": "7.8 L/hr",
  "8": "8 L/hr",
  "11.2": "11.2 L/hr",
  "12": "12 L/hr",
  "12.4": "12.4 L/hr",
  "16": "16 L/hr",
};
const formatRPM: (value: string | number) => string = (value: string | number): string =>
  rpmLabels[Number(value)] ?? "";
const formatFuel: (value: number) => string = (value: number): string => fuelLabels[value] ?? "";
const config = {
  port: { label: "Port engine", formatValue: formatFuel },
  starboard: { label: "Starboard engine", formatValue: formatFuel },
};
export default function EngineObservations(): ReactElement {
  return (
    <ChartPanel
      title="Fuel consumption and RPM"
      description="Illustrative paired measurements"
      legend={<ChartLegend config={config} label="Engine observations" mark="point" />}
    >
      <ScatterChart
        data={[
          { rpm: 1800, port: 7.1, starboard: 7.8 },
          { rpm: 1200, port: 3.2, starboard: 3.8 },
          { rpm: 1800, port: 7.6, starboard: null },
          { rpm: 2200, port: 11.2, starboard: 12.4 },
        ]}
        categoryKey="rpm"
        categoryDomain={[1000, 2500]}
        valueDomain={[0, 16]}
        config={config}
        series={["port", "starboard"]}
        formatCategory={formatRPM}
        formatAxisValue={formatFuel}
        label="Illustrative fuel consumption versus RPM"
        categoryLabel="RPM"
        dataTableLabel="View paired observations"
        missingValueText="Unavailable"
        describeObservation={(): string => "Same observation time · illustrative"}
      />
    </ChartPanel>
  );
}

Props

PropTypeDefaultDescription
calloutChartCallout | undefined–A visible summary bubble with caller-formatted value and context.
onCategoryDomainChange((range: readonly [number, number]) => void) | undefined–Receives a numeric viewport after drag-to-zoom or Shift-drag pan; requires categoryDomain.
maxGapnumber | undefined–Maximum elapsed distance connected by a line; numeric categories only.
syncIdstring | undefined–Charts sharing this identifier synchronize inspection by category value or nearest time.
syncTolerancenumber | undefined–Maximum distance for synchronized numeric categories; zero requires exact matches.
interpolation"linear" | "stepAfter" | undefined–Linear segments or step changes; linear by default. Missing values remain gaps.
datarequiredreadonly T[]–Rows in display order; numeric time categories must be increasing.
categoryKeyrequiredChartCategoryKey<NoInfer<T>>–The row field naming a category or locating a sample on a numeric axis.
configrequiredChartConfig<NoInfer<T>>–Definitions for numeric series, shared with legends and controls.
seriesrequiredreadonly ChartValueKey<NoInfer<T>>[]–The visible numeric fields, in display order. Filtering preserves the complete registry's domain.
formatCategoryrequired(value: string | number) => string–Writes category ticks, tooltip headings and the first table column.
formatAxisValuerequired(value: number) => string–Writes value-axis ticks; separate from each series' observation formatter.
labelrequiredstring–The drawing's accessible name, naming the quantity and time window.
categoryLabelrequiredstring–The first table column's heading, translated by the caller.
dataTableLabelrequiredstring–The disclosure button and source table's accessible name, translated by the caller.
missingValueTextrequiredstring–Display text for missing values in the tooltip and source table.
heightstring | undefined–The plot height as a CSS length; the chart height token by default.
valueDomainreadonly [number, number] | undefined–An explicit increasing value domain that clips outlying values; bar domains always include zero.
showGridboolean | undefinedtrueDraw the grid; on by default.
showAxesboolean | undefinedtrueDraw axes; on by default. Values remain available in the table when off.
motionMotionProp | undefined–Entrance and exit motion; off by default. Measurements never interpolate.
childrenReactNode | undefined–Recharts annotations appended inside the plot, such as ReferenceLine. Use ChartContainer for a custom root.
classNamestring | undefined–Classes added to the root.
testIdstring | undefined–Becomes data-testid on the root, with --plot and --table parts.
refRef<HTMLDivElement> | undefined–The chart root element.
categoryDomainreadonly [number, number] | undefined–An increasing numeric x domain; inferred from all observations when unset.
describeObservation((row: T) => string) | undefined–Writes point identity, pairing tolerance and source details into the source table.
Do

Fuel consumption and RPM

Illustrative paired measurements

  • Port engine
  • Starboard engine
Keep the comparison basis and missing information visible.
Don't

Preserve gaps and supply the source or validity information needed to interpret the observations.

Missing history must not appear to be a measured zero or a known state.