React Time-Series Brush

A day of per-minute telemetry is 1,440 points — too many to read at once, too few to throw away. The overview keeps the whole shape in view; drag the window and the detail chart above resolves to it. Grab an edge to resize, the middle to pan.

Chamber pressure — 24 h

14:2317:16 · 173 pts

755761768Torr14:2315:4917:16

Drag the window or its edges to zoom

Installation

npx shadcn@latest add "https://kelvinui.com/registry/timeseries-brush.json"

Props

PropTypeDescription
dataTimeseriesPoint[]{ t, value }, t monotonic (epoch ms works)
defaultWindow[number, number]Initial window as [startFrac, endFrac] of range
formatX(t: number) => stringAxis and readout formatter
unitstringAppended to the y readout
colorstringDetail line colour
heightnumberDetail SVG height (default 220)

Pointer capture, not a library

The brush is plain SVG with pointer-capture drag handling — three modes (move, start, end) tracked in a ref, resolved against the overview's bounding box in fractional coordinates. There is no charting or gesture dependency, so it ships small and behaves the same under React strict mode.

Two details that make it feel right: the window enforces a minimum width so the edges never cross, and dragging the middle clamps to [0, 1] while preserving width, so panning to the end doesn't shrink your selection. The detail chart re-scales its y-axis to whatever is inside the window — zooming into a quiet stretch still fills the vertical space, which is usually what you want when hunting for a small excursion.

For a single fixed-resolution series, a plain line chart is enough; reach for this when the series is long enough that the whole-range view and the readable-detail view genuinely conflict.

New components every week

Get the week's new Kelvin UI components and templates in one short email. No spam, unsubscribe anytime.