DocumentationComponentsTheme Creator
Theme CreatorGitHubIntroduction
InstallationUsageThemingMotion
Components
ChartArea ChartBar ChartLine ChartLive Line ChartComposed ChartPie ChartFunnel ChartRadar ChartRadial ChartSankey ChartScatter ChartCandlestick ChartSunburst ChartChoropleth ChartActivity Heatmap
AI agentsContributing
  1. Components
  2. Live Line Chart

Live Line Chart

A real-time line chart — streamed readings over a sliding time window, a live dot, a crosshair and a pause, with throttled screen-reader updates and a stepped reduced-motion mode.

$ pnpm add @tessinaui/ui

When to use

  • One value read as it arrives. A price, a latency, requests per second, a model's tokens per second — where the reader watches the value move.
  • A recent window, not a history. The chart shows the last seconds to the last hour and lets the rest scroll away.
  • When someone needs to stop and read. paused freezes the window while readings keep arriving, and the chart catches up on resume.

When not to use

  • For a fixed history. Last month's revenue, last week's sessions: use LineChart. A live chart redraws every frame for data that does not move.
  • For several live series. This chart draws one value. Compare two live values in two charts side by side, or snapshot them into a LineChart.
  • When only the latest number matters. A readout, a Meter or a Badge says it with nothing moving.
  • For a value that changes every few minutes. Poll and append to a LineChart; a sliding window of an hour with four readings is mostly empty.

Installation

Terminal
pnpm add @tessinaui/ui recharts

Usage

example.tsx
import * as React from "react";
import { LiveLineChart, type LiveLineChartPoint } from "@tessinaui/ui";

export function Throughput({ feed }: { feed: EventSource }) {
  const [data, setData] = React.useState<LiveLineChartPoint[]>([]);

  React.useEffect(() => {
    const onReading = (event: MessageEvent) => {
      const value = Number(event.data);
      // time in epoch milliseconds; keep your own array bounded too.
      setData((points) => [...points, { time: Date.now(), value }].slice(-1000));
    };
    feed.addEventListener("message", onReading);
    return () => feed.removeEventListener("message", onReading);
  }, [feed]);

  return (
    <LiveLineChart
      data={data}
      window={30}
      valueFormatter={(value) => `${value} tok/s`}
      label="Generation speed, tokens per second"
    />
  );
}

time is epoch milliseconds — what Date.now() returns. Append readings in arrival order; the chart draws only what is inside the window, at most maxPoints of them.

Examples

Default

Press Start to run the feed. Pause freezes the window while readings keep arriving; Resume jumps to the present.

Momentum

momentum colours the line by its direction across the window. Say the direction in words or with a sign beside the chart, as here: the colour must never be the only cue.

Threshold

children are drawn inside the chart. A ReferenceLine marks a target the reading should stay under.

Dense feed

Fifty readings a second over a 20-second window: a thousand on screen, the maxPoints default. The chart keeps the newest maxPoints and drops what has scrolled away, so a fast feed costs no more to draw than the window holds.

Sizes

size sets the aspect ratio: sm is 3:1, md 2:1 and lg 16:9.

On a coloured plate

Inside a coloured Surface, give the line the plate's ink. The axes, grid, crosshair, live dot and focus ring follow the plate on their own.

States

loading while the feed connects, emptyState before the first reading and error when the feed is lost.

API Reference

PropTypeDefaultDescription
data{ time: number; value: number }[]—Readings, oldest first, time in epoch milliseconds.
windownumber30How much time the chart shows, in seconds.
pausedbooleanfalseFreeze the window to read it. Readings keep arriving; the chart jumps to the present on resume.
maxPointsnumber1000The most readings the chart keeps.
liveDotbooleantrueMark the newest reading. A halo pings from it while the chart runs.
momentumbooleanfalseColour the line by its direction across the window.
configChartConfigvar(--chart-1), "Value"The series' name and colour, keyed value.
size"sm" | "md" | "lg""md"Aspect ratio.
showGridbooleantrueHorizontal grid lines.
showXAxisbooleantrueThe time axis.
showYAxisbooleantrueThe value axis.
showTooltipbooleantrueThe crosshair and its tooltip.
yAxisPosition"left" | "right"the reading endDefaults to the side the newest reading arrives on.
autoMinValuebooleantrueFit the value axis to the readings in view instead of anchoring it at zero.
minValue / maxValuenumber—Pin an end of the value axis.
valueFormatter(value: number) => stringtoLocaleString()Formats a value on the axis, in the tooltip and when spoken.
timeFormatter(time: number) => stringHH:MM:SSFormats a time on the axis and in the tooltip.
stringsPartial<LiveLineChartStrings>English, Arabic under rtlSpoken and empty-state text.
labelstring—Accessible name; renders the chart as a role="figure".
descriptionstring—Visually hidden summary for a static read.
dir"ltr" | "rtl"from the pagertl runs time right to left and moves the value axis to the left.
loadingbooleanfalseShow the loading skeleton.
errorReactNode—Show an error message instead of the chart.
emptyStateReactNode"No data to display"Shown when data is empty.
childrenReactNode—Extra Recharts children, plotted on time.

Strings

Pass any of these in strings to replace one sentence. {value} is the reading, formatted by valueFormatter.

KeyDefaultWhen
latest"Latest value {value}"Spoken at most every 10 seconds while the chart runs, only when the value changed.
paused"Paused at {value}"Spoken once when paused turns on.
live"Live"Spoken once when paused turns off.
value"Value"The series name in the tooltip when config gives none.
empty"No data to display"Shown when data is empty and there is no emptyState.
example.tsx
<LiveLineChart data={data} strings={{ latest: "Now {value}", live: "Streaming" }} />

Motion

The window scrolls every frame while the chart runs; a new reading is never animated in, because a draw-in restarts on each one. Ticks sit on whole seconds, so a label keeps its value while it slides.

Under prefers-reduced-motion: reduce nothing scrolls: the window steps once a second and the moment a reading arrives, and the live dot's halo does not ping. A browser tab in the background stops drawing; the chart jumps to the present when the tab returns.

Accessibility

VoiceOver: not yet tested.

  • Name the insight. label becomes the chart's accessible name. Say what the value is doing ("p95 latency crossed its 200 ms target twice"), not that it is a live chart.
  • Updates are throttled. A polite live region beside the chart says the latest value at most every ten seconds, and only when it changed. A pause and a resume are said at once ("Paused at 182 req/s", "Live"). The first render is silent.
  • The tooltip waits for a pause. It is a polite live region too, so while the chart runs it is silenced: a reading under a held pointer changes constantly. Pause to read point by point.
  • Keyboard. Tab reaches the plot; the arrow keys step the crosshair from reading to reading and Escape hides the tooltip. Put a pause control next to the chart, as the examples do, so a keyboard user can stop it first.
  • Colour is never the only cue. momentum repeats the line's direction; say it in text beside the chart as well.

Theming

TokenUsed for
--chart-1The default line colour
--success-border / --error-bordermomentum up and down on the page
--muted-foregroundTicks, grid, crosshair, and a flat momentum
--on-ink-success / --on-ink-errormomentum on a coloured plate
Line ChartComposed Chart

On this page

When to useWhen not to useInstallationUsageExamplesDefaultMomentumThresholdDense feedSizesOn a coloured plateStatesAPI ReferenceStringsMotionAccessibilityTheming