Components / Charts

Streamgraph

Layered streams on a wiggle baseline that morph between ranges, with a layer you can isolate and read week by week.

Install

npx shadcn@latest add https://irisui.dev/r/streamgraph.json
import { Streamgraph } from "@/components/iris/streamgraph/streamgraph";
Registry item streamgraphBrings in foundationRegistry JSON

Preview

Streamgraph

Layered streams on a wiggle baseline that show how a mix of parts changes over time, with layers you can isolate and read week by week.

When to use

  • How the composition of a total shifts over many periods, when the overall rhythm matters more than exact values: tickets by topic, usage by feature, traffic by source.
  • Spotting when a new part arrives or swells, such as a launch wave.
  • Not when exact comparisons matter; use LineChart. For a few periods or categories, use BarChart. Avoid it for negative values.

Usage

import { Streamgraph } from "@/components/iris/streamgraph/streamgraph";

const series = [
  { key: "en", label: "English" },
  { key: "it", label: "Italian" },
  { key: "el", label: "Greek" },
];
const data = [
  { key: "2026-10-05", label: "Week of 5 Oct", axisLabel: "Oct", values: { en: 124, it: 112, el: 88 } },
  { key: "2026-10-12", label: "Week of 12 Oct", values: { en: 131, it: 108, el: 94 } },
  { key: "2026-10-19", label: "Week of 19 Oct", values: { en: 119, it: 116, el: 91 } },
];

h(Streamgraph, { data, series, label: "Stories by language", unit: "stories", categoryLabel: "Week" });

Props

Prop Type Default Description
data { key; label; axisLabel?; values: Record<string, number> }[] required Points in time. Missing values count as zero; negatives as zero. Datasets of different lengths morph into each other.
series { key; label; color? }[] required Layers from the centre out: the first runs through the middle, the rest alternate above and below. Put the most important layer first.
label string required Names the chart for assistive technology.
unit string "" Unit after each value.
height number 260 Plot height; width follows the container.
offset "wiggle" | "silhouette" | "zero" "wiggle" Baseline: wiggle minimises slopes, silhouette centres the stack, zero stacks from a flat baseline (use when totals matter).
formatValue (value, series) => string — Tooltip and table values.
hiddenSeries / defaultHiddenSeries / onHiddenSeriesChange string[] / callback — Hidden layers; a hidden layer thins to nothing and the rest reflow.
onActiveChange (index, seriesKey) => void — Fires as the reading moves; nulls when it leaves.
legend boolean true Layer toggles above the plot; hover or focus previews a layer.
directLabels boolean true Names each layer at its widest stretch when the text fits.
categoryLabel / emptyLabel string "Date" / "No data for this range" Hidden table header; empty state.
className string — Class on the root figure.
ref Ref<HTMLElement> — Forwarded to the root figure.

Notes

  • Keyboard: Tab focuses the plot on the latest point. Left/Right move through time, Up/Down move the isolated layer through the stack, Page Up/Down and Home/End jump, Escape clears.
  • Accessibility: the plot is a focusable group; each reading announces the point, the isolated layer's value and the total. Legend toggles use aria-pressed; a hidden table lists every value. Layers are separated by a 2px --surface gap and named in place, so identity does not rest on shade alone.
  • Colours come from --series-1…4 (first in the accent), then neutral steps. Keep to about eight layers and fold the rest into Other.
  • Motion: layers morph between datasets; reduced motion swaps them without animation.