Live Line Chart

A streaming line chart — a time window that slides in real time over data as it arrives, with an interpolated live tip, a crosshair that reads between samples, and axes that glide with the domain.

Requests 1,200

Installation

pnpm add @blakeui/pro-react
npm install @blakeui/pro-react
yarn add @blakeui/pro-react
bun add @blakeui/pro-react

Usage

LiveLineChart is built on visx time scales and the motion library. It is a single component: grid, axes, tooltip, live dot, and momentum colouring are all props, not composable children.

You own the stream. The chart holds no interval and generates no data — it takes a growing data array and renders a moving window over it. Append points as they arrive from your WebSocket, poll, or SSE connection.

import {LiveLineChart} from "@blakeui/pro-react";

const [data, setData] = useState([]);
const [value, setValue] = useState(0);

useEffect(() => {
  const id = setInterval(() => {
    const next = readLatestMetric();
    setData((previous) => [...previous, {time: Date.now() / 1000, value: next}].slice(-240));
    setValue(next);
  }, 500);
  return () => clearInterval(id);
}, []);

<LiveLineChart data={data} value={value} window={30} />;

Data and value

data is an array of {time, value} sorted oldest-first, where time is Unix seconds — not milliseconds. value is passed separately, and that separation is the point: data is committed history, while value is the target the live tip eases toward. Between arrivals the dot keeps moving toward value instead of sitting still and then jumping, which is what makes the chart read as live rather than as a chart that redraws twice a second.

Updating value without appending to data is a normal, supported case — the tip, its readout, and the dashed value rule all move; the committed line does not.

Cap your buffer

The chart never mutates data, so nothing stops it growing forever. Slice it on every append. A good default is a few multiples of the visible window — enough history that a paused chart still has real data behind it when it resumes, without unbounded growth on a page left open.

The time window

window is the visible span in seconds; older points scroll off the left edge. Rendering cost is set by what is on screen, not by how much history you hold — the chart binary-searches to the window start, so passing 10,000 points costs the same as passing 100.

nowOffsetUnits leaves a leading gap between the live tip and the right edge, measured in x-tick units. At the default numXTicks={5} one unit is a quarter of the plot, so nowOffsetUnits={1} puts the tip at 75% width and fades the line out across the gap ahead of it. At the default of 0 the tip sits hard against the right edge and there is no leading fade.

Pausing

Set paused to freeze the window. The "now" marker and the domain stop advancing, but new data still appends and still widens the y-domain — so nothing is lost while paused. Releasing it snaps the domain forward to the current time rather than replaying the backlog.

Momentum

Set momentum to colour the line, area, and live dot by short-term trend: --chart-1 while rising, --chart-2 while falling, and the base stroke when flat. The trend compares the last five samples' net change against 12% of the local peak-to-trough range, so a quiet series reads as flat instead of flickering on noise.

Motion

The window slides on an animation frame loop, independent of when data arrives, so the chart scrolls smoothly rather than stepping. Interpolation is time-based, not per-frame, which keeps the scroll rate identical across refresh rates and through dropped frames. The y-domain is deliberately asymmetric: it snaps open the instant a new extreme arrives, so an incoming spike is never clipped, and eases closed when that extreme scrolls away, so the chart does not lurch every time a peak leaves the window.

The loop is suspended while the chart is scrolled out of view and snaps to the current state when it returns — an off-screen chart neither burns frames nor accumulates a backlog to animate through.

Under prefers-reduced-motion all continuous motion is suppressed: no sliding window, no interpolation between arrivals, no pulsing dot. The chart still steps as each point lands. A live chart has no final state to snap to, and freezing it would hide current data — accessibility must not cost the reader information.

The chart exposes the live value, and the hovered value while scrubbing, to assistive tech through a polite live region. Left/right arrows move the crosshair, Home and End jump to the window edges, and Escape clears it.

Reading the crosshair

The hovered value is interpolated along the line rather than snapped to the nearest sample, and it is re-read every frame. A stationary pointer therefore reports a changing time and value as the window slides beneath it — that is the chart being live, not the tooltip drifting.

Connecting state

LiveLineChart has no loading status of its own — a live chart's loading state is just a line. Render a loading LineChart while the stream connects, then swap in the live chart once data starts arriving.

Connecting…

API Reference

LiveLineChart

The single chart component. Measures its container, slides a time-domained window over data, interpolates the live tip toward value, and renders the series, axes, hover layer, and readout.

PropTypeDefaultDescription
dataLiveLineChartPoint[]Streaming samples, oldest first: {time, value} with time in Unix seconds. The chart never mutates or caps this array — you own it.
valuenumberThe latest value, separate from data so the live tip can interpolate toward it between arrivals instead of stepping.
windownumber30Visible time span in seconds. Older points scroll off the left edge.
numXTicksnumber5Number of x tick labels. Also sets the size of one nowOffsetUnits unit.
nowOffsetUnitsnumber0Leading gap between the live tip and the right edge, in x-tick units. 1 at the default tick count leaves a quarter-plot gap that the line fades into.
pausedbooleanfalseFreeze the window. New data still appends and still widens the y-domain.
exaggeratebooleanfalseTighten the y-domain padding from 15% to 3%, exaggerating small moves.
strokestring"var(--chart-1)"Series color. Ignored for the line, area, and dot while momentum is active.
strokeWidthnumber2Series stroke width.
curveLiveLineCurvecurveMonotoneXD3 curve interpolation factory.
momentumbooleanfalseColour the line, area, and dot by short-term trend — --chart-1 rising, --chart-2 falling, stroke flat.
showAreabooleantrueRender the gradient fill under the line.
showGridbooleantrueRender horizontal grid lines, aligned to the y tick values.
showAxesbooleantrueRender the x and y axis labels.
showTooltipbooleantrueRender the hover crosshair and the floating tooltip.
showLiveDotbooleantrueRender the pulsing live dot, its rolling readout, and the dashed value rule.
valueFormatter(value: number) => stringFormats axis tick labels and the screen-reader readout. It does not format the live value or the tooltip value — those always roll through the odometer.
timeFormatter(date: Date) => stringHH:MM:SSFormats x tick labels and the tooltip header.
valuePrefixstringStatic text rendered before the rolling value (e.g. "$").
valueLabelstring"Value"Series name shown in the tooltip and announced to assistive tech.
aspectRatiostring"2 / 1"CSS aspect ratio of the chart box. Ignored when height is set.
heightnumberFixed pixel height. Overrides aspectRatio.
marginPartial<Margin>{top: 16, right: 16, bottom: 32, left: 56}Plot margins — a left gutter for value labels, room below for time labels.

Also supports all native div HTML attributes.

LiveLineChartPoint

Each entry in data is one sample of the stream.

FieldTypeDescription
timenumberUnix time in secondsDate.now() / 1000, not Date.now(). Points must be sorted oldest-first.
valuenumberThe sample's value. Participates in the y-domain while it is inside the window.

On this page