Area chart
Show amounts across a window as overlapping or stacked areas, with typed intervals and accessible source values.
On this page
Daily work
Illustrative data · Monday through Friday
- Planned work
- Repairs
Code
import { AreaChart } from "@bosunkit/components/area-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import type { ChartConfig } from "@bosunkit/components/chart-container";
import type { ReactElement } from "react";
export const description: string =
"Illustrative category amounts as overlapping areas; Wednesday has an unmeasured category.";
interface Amount {
readonly day: string;
readonly planned: number | null;
readonly repairs: number | null;
}
const data: readonly Amount[] = [
{ day: "Mon", planned: 4, repairs: 2 },
{ day: "Tue", planned: 5, repairs: 3 },
{ day: "Wed", planned: 6, repairs: null },
{ day: "Thu", planned: 3, repairs: 2 },
{ day: "Fri", planned: 5, repairs: 1 },
];
const values: Readonly<Record<number, string>> = {
0: "0",
1: "1",
2: "2",
3: "3",
4: "4",
5: "5",
6: "6",
7: "7",
8: "8",
9: "9",
10: "10",
};
const writeValue: (value: number) => string = (value: number): string => values[value] ?? "";
const writeDay: (value: string | number) => string = (value: string | number): string =>
typeof value === "string" ? value : "";
const config: ChartConfig<Amount> = {
planned: { label: "Planned work", formatValue: writeValue },
repairs: { label: "Repairs", formatValue: writeValue },
};
export default function DailyAmounts(): ReactElement {
return (
<ChartPanel
title="Daily work"
description="Illustrative data · Monday through Friday"
legend={<ChartLegend config={config} label="Work categories" />}
footer="Items per day · incomplete stacks have no drawn total"
>
<AreaChart
data={data}
categoryKey="day"
config={config}
series={["planned", "repairs"]}
mode="overlapping"
valueDomain={[0, 10]}
formatCategory={writeDay}
formatAxisValue={writeValue}
label="Illustrative work amounts, Monday through Friday"
categoryLabel="Day"
dataTableLabel="View daily amounts"
missingValueText="Not counted"
/>
</ChartPanel>
);
}
An area chart shows amounts through the extent of a filled shape. Overlapping areas compare series; stacked areas show composition. Every area includes zero in its value scale. A line chart is more suitable when a nonzero baseline helps compare changes in pressure, temperature or another level.
Use
The registry supplies labels, categorical palette slots, patterns and observation formatters. series selects visible keys without changing the complete registry's comparison scale. categoryScale="time" accepts increasing epoch milliseconds and preserves elapsed spacing. The caller formats every quantity and time, including the time reference.
bands describes lower and upper numeric fields around a series, with a display label and formatRange. Missing bounds leave a gap; reversed finite bounds are rejected. Bands follow their associated series' visibility and widen an automatic domain. They apply to overlapping areas; individual uncertainty intervals do not describe a stacked total.
A forecast is a separate, dashed series after the historical window. Label its quantiles and model through caller display text, and mark its start with a Recharts ReferenceLine. Quantile bounds are not observed minima and maxima. The chart draws supplied values and performs no forecasting or filtering.
Examples
Overlapping
Daily work
Illustrative data · Monday through Friday
- Planned work
- Repairs
Code
import { AreaChart } from "@bosunkit/components/area-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import type { ChartConfig } from "@bosunkit/components/chart-container";
import type { ReactElement } from "react";
export const description: string =
"Illustrative category amounts as overlapping areas; Wednesday has an unmeasured category.";
interface Amount {
readonly day: string;
readonly planned: number | null;
readonly repairs: number | null;
}
const data: readonly Amount[] = [
{ day: "Mon", planned: 4, repairs: 2 },
{ day: "Tue", planned: 5, repairs: 3 },
{ day: "Wed", planned: 6, repairs: null },
{ day: "Thu", planned: 3, repairs: 2 },
{ day: "Fri", planned: 5, repairs: 1 },
];
const values: Readonly<Record<number, string>> = {
0: "0",
1: "1",
2: "2",
3: "3",
4: "4",
5: "5",
6: "6",
7: "7",
8: "8",
9: "9",
10: "10",
};
const writeValue: (value: number) => string = (value: number): string => values[value] ?? "";
const writeDay: (value: string | number) => string = (value: string | number): string =>
typeof value === "string" ? value : "";
const config: ChartConfig<Amount> = {
planned: { label: "Planned work", formatValue: writeValue },
repairs: { label: "Repairs", formatValue: writeValue },
};
export default function DailyAmounts(): ReactElement {
return (
<ChartPanel
title="Daily work"
description="Illustrative data · Monday through Friday"
legend={<ChartLegend config={config} label="Work categories" />}
footer="Items per day · incomplete stacks have no drawn total"
>
<AreaChart
data={data}
categoryKey="day"
config={config}
series={["planned", "repairs"]}
mode="overlapping"
valueDomain={[0, 10]}
formatCategory={writeDay}
formatAxisValue={writeValue}
label="Illustrative work amounts, Monday through Friday"
categoryLabel="Day"
dataTableLabel="View daily amounts"
missingValueText="Not counted"
/>
</ChartPanel>
);
}
Stacked
Daily work
Illustrative data · Monday through Friday
- Planned work
- Repairs
Code
import { AreaChart } from "@bosunkit/components/area-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import type { ChartConfig } from "@bosunkit/components/chart-container";
import type { ReactElement } from "react";
export const description: string =
"Illustrative category amounts as vertical stacked bars; Wednesday has an unmeasured category.";
interface Amount {
readonly day: string;
readonly planned: number | null;
readonly repairs: number | null;
}
const data: readonly Amount[] = [
{ day: "Mon", planned: 4, repairs: 2 },
{ day: "Tue", planned: 5, repairs: 3 },
{ day: "Wed", planned: 6, repairs: null },
{ day: "Thu", planned: 3, repairs: 2 },
{ day: "Fri", planned: 5, repairs: 1 },
];
const values: Readonly<Record<number, string>> = {
0: "0",
1: "1",
2: "2",
3: "3",
4: "4",
5: "5",
6: "6",
7: "7",
8: "8",
9: "9",
10: "10",
};
const writeValue: (value: number) => string = (value: number): string => values[value] ?? "";
const writeDay: (value: string | number) => string = (value: string | number): string =>
typeof value === "string" ? value : "";
const config: ChartConfig<Amount> = {
planned: { label: "Planned work", formatValue: writeValue },
repairs: { label: "Repairs", formatValue: writeValue },
};
export default function DailyAmounts(): ReactElement {
return (
<ChartPanel
title="Daily work"
description="Illustrative data · Monday through Friday"
legend={<ChartLegend config={config} label="Work categories" />}
footer="Items per day · incomplete stacks have no drawn total"
>
<AreaChart
data={data}
categoryKey="day"
config={config}
series={["planned", "repairs"]}
mode="stacked"
valueDomain={[0, 10]}
formatCategory={writeDay}
formatAxisValue={writeValue}
label="Illustrative work amounts, Monday through Friday"
categoryLabel="Day"
dataTableLabel="View daily amounts"
missingValueText="Not counted"
/>
</ChartPanel>
);
}
Observations and forecast quantiles
Observed and forecast
Illustrative amounts · observations end at 14:20 UTC
- Observed
- Forecast
Code
import { AreaChart } from "@bosunkit/components/area-chart";
import { ChartLegend } from "@bosunkit/components/chart-legend";
import { ChartPanel } from "@bosunkit/components/chart-panel";
import type { ChartConfig, ChartBand } from "@bosunkit/components/chart-container";
import * as Recharts from "recharts";
import type { ReactElement } from "react";
export const description: string =
"Illustrative observed and forecast amounts have separate series, a dashed forecast, a cutoff and explicit 10th–90th quantile bounds.";
interface Sample {
readonly time: number;
readonly observed: number | null;
readonly forecast: number | null;
readonly q10: number | null;
readonly q90: number | null;
}
const start: number = 1791986400000;
const step: number = 600000;
const data: readonly Sample[] = [
{ time: start, observed: 6, forecast: null, q10: null, q90: null },
{ time: start + step, observed: 5, forecast: null, q10: null, q90: null },
{ time: start + step * 2, observed: 6, forecast: null, q10: null, q90: null },
{ time: start + step * 3, observed: null, forecast: 7, q10: 6, q90: 8 },
{ time: start + step * 4, observed: null, forecast: 8, q10: 6, q90: 10 },
{ time: start + step * 5, observed: null, forecast: 9, q10: 5, q90: 10 },
];
const values: Readonly<Record<number, string>> = {
0: "0 items",
1: "1 item",
2: "2 items",
3: "3 items",
4: "4 items",
5: "5 items",
6: "6 items",
7: "7 items",
8: "8 items",
9: "9 items",
10: "10 items",
};
const times: Readonly<Record<number, string>> = {
[start]: "14:00 UTC",
[start + step]: "14:10 UTC",
[start + step * 2]: "14:20 UTC",
[start + step * 3]: "14:30 UTC",
[start + step * 4]: "14:40 UTC",
[start + step * 5]: "14:50 UTC",
};
const write: (value: number) => string = (value: number): string => values[value] ?? "";
const writeTime: (value: string | number) => string = (value: string | number): string =>
typeof value === "number" ? (times[value] ?? "") : "";
const config: ChartConfig<Sample> = {
observed: { label: "Observed", formatValue: write, pattern: "solid" },
forecast: { label: "Forecast", formatValue: write, pattern: "dashed" },
};
const ranges: Readonly<Record<string, string>> = { "6:8": "6–8 items", "6:10": "6–10 items", "5:10": "5–10 items" };
const bands: readonly ChartBand<Sample>[] = [
{
series: "forecast",
low: "q10",
high: "q90",
label: "10th–90th forecast quantiles",
formatRange: (low: number, high: number): string => ranges[`${String(low)}:${String(high)}`] ?? "",
},
];
export default function ForecastWindow(): ReactElement {
return (
<ChartPanel
title="Observed and forecast"
description="Illustrative amounts · observations end at 14:20 UTC"
legend={<ChartLegend config={config} label="Observation and forecast series" />}
footer="Illustrative only · band shows supplied 10th–90th quantiles, not observed extrema"
>
<AreaChart
data={data}
categoryKey="time"
categoryScale="time"
config={config}
series={["observed", "forecast"]}
bands={bands}
formatCategory={writeTime}
formatAxisValue={write}
valueDomain={[0, 10]}
label="Illustrative observations and forecast, 14:00 to 14:50 UTC"
categoryLabel="Time, UTC"
dataTableLabel="View observations and quantiles"
missingValueText="Not supplied"
>
<Recharts.ReferenceLine x={start + step * 3} label={{ value: "Forecast starts", position: "insideTopRight" }} />
</AreaChart>
</ChartPanel>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| mode | "overlapping" | "stacked" | undefined | "overlapping" | Draw overlapping areas or a composition; overlapping by default. |
| categoryScale | "category" | "linear" | "time" | undefined | – | Category spacing, numeric spacing, or epoch milliseconds on a time scale; category by default. |
| bands | readonly ChartBand<NoInfer<T>>[] | undefined | – | Typed intervals around visible series; gaps remain gaps. Stacked areas do not accept intervals. |
| callout | ChartCallout | 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. |
| maxGap | number | undefined | – | Maximum elapsed distance connected by a line; numeric categories only. |
| categoryDomain | readonly [number, number] | undefined | – | Controlled numeric/time viewport; emits no requests by itself. |
| syncId | string | undefined | – | Charts sharing this identifier synchronize inspection by category value or nearest time. |
| syncTolerance | number | 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. |
| describeObservation | ((row: T) => string) | undefined | – | Caller-formatted source, processing stage or quality shown alongside every source-table row. |
| datarequired | readonly T[] | – | Rows in display order; numeric time categories must be increasing. |
| categoryKeyrequired | ChartCategoryKey<NoInfer<T>> | – | The row field naming a category or locating a sample on a numeric axis. |
| configrequired | ChartConfig<NoInfer<T>> | – | Definitions for numeric series, shared with legends and controls. |
| seriesrequired | readonly 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. |
| labelrequired | string | – | The drawing's accessible name, naming the quantity and time window. |
| categoryLabelrequired | string | – | The first table column's heading, translated by the caller. |
| dataTableLabelrequired | string | – | The disclosure button and source table's accessible name, translated by the caller. |
| missingValueTextrequired | string | – | Display text for missing values in the tooltip and source table. |
| height | string | undefined | – | The plot height as a CSS length; the chart height token by default. |
| valueDomain | readonly [number, number] | undefined | – | An explicit increasing value domain that clips outlying values; bar domains always include zero. |
| showGrid | boolean | undefined | – | Draw the grid; on by default. |
| showAxes | boolean | undefined | – | Draw axes; on by default. Values remain available in the table when off. |
| motion | MotionProp | undefined | – | Entrance and exit motion; off by default. Measurements never interpolate. |
| children | ReactNode | undefined | – | Recharts annotations appended inside the plot, such as ReferenceLine. Use ChartContainer for a custom root. |
| className | string | undefined | – | Classes added to the root. |
| testId | string | undefined | – | Becomes data-testid on the root, with --plot and --table parts. |
| ref | Ref<HTMLDivElement> | undefined | – | The chart root element. |
Observed and forecast
Illustrative amounts · observations end at 14:20 UTC
- Observed
- Forecast
Name the quantile levels and retain the model and time reference in the surrounding panel.
Related
- Instrument history: raw and filtered streams, forecasts and asynchronous windows.
- Line chart: compare levels without implying a zero-based filled amount.
- Chart panel: titles, controls, legends and source information.