HeartbeatBlip
React heartbeat blip chart — recent events as blips in a live window. Tiny accessible SVG microchart; RSC-safe static or /interactive.
HeartbeatBlip answers "is it alive, and how busy?" at a glance. Each event in the recent window is an ECG-style spike; in the interactive entry the trace sweeps left in real time and a new event blips in at the right edge, so the rate you see is the event rate. Every spike is one real event — never a synthesized pulse on a timer — and an empty, flat baseline is the down signal, carried by shape, not color.
Install
import { HeartbeatBlip } from "@microcharts/react/heartbeat-blip";// pass 'now' from your data layer — never Date.now() in a server render// eventTimestamps, serverNow — real values under “Sample data” below<HeartbeatBlip events={eventTimestamps} now={serverNow} title="Requests" />Sample data
const eventTimestamps = [97_000, 92_000, 85_000, 70_000, 55_000, 48_000];const serverNow = 100_000;Try it
Motion, and reduced motion
The trace advances in real time so old spikes drift left and new events enter at the right — the liveness comes from
watching the blips arrive. This is the deliberate idle-loop exception, allowed because the loop parameter (elapsed time)
is the datum. It pauses off-screen (a shared observer) and, under prefers-reduced-motion, does not sweep at all: the
static strip simply re-renders on each data change — the same information, discretely updated. The one rule this chart
must never break: a flat service never gets a fake pulse. Every spike is a real event; when the window empties, the
baseline goes flat and stays flat.
When to use it
- Good for — at-a-glance liveness of a service or stream, request rate in a header, or per-service liveness in a status table.
- Avoid for — exact event counts (
Seismogram,EventTimeline), a continuous level (BreathingDot), or long-term trends (Sparkline).
Sizing
width and height are viewBox units that also set the rendered pixel box — they default to 60 × 16, sized to sit in
a status table row or a header. Omit them and drive the width from CSS to fill a column; the viewBox keeps the aspect
ratio. window sets how much time the strip covers, which changes the density of the trace rather than its box.
Variants
format/locale only reach the label="count" numeral — the accessible summary's count is a plain integer (never run
through the formatter), and the in-chart spikes never carry text, so there's nothing else to localize.
Edge cases
Four homes
Why this default
The 60-second window is the default because liveness questions are about the last minute. The now clock is a
required-in-spirit prop: the static entry must never call Date.now() (a server render and the client hydrate would
disagree and mismatch), so you pass the clock from your data layer. Flatline is never dressed up — down and no-data are
different states, and the summary distinguishes them. The boundary with BreathingDot: discrete arriving events are a
HeartbeatBlip; a continuous level is a BreathingDot.
Accessibility
The accessible name is the count, the window, and the time since the last event — "3 events in the last minute; last 3s ago." — or "No events in the last minute." when the trace is flat. The trace sweeps only when motion is allowed and on-screen; the live region announces on data change, and there is no per-spike navigation because the spikes are transient — the summary is the record.
This chart is a single unit, so there is nothing to rove between: a click, tap, Enter or Space selects it
and fires onSelect, and no selection stays pinned. That is the scalar half of the shared
interaction contract.
Props
| Prop | Type | Notes |
|---|---|---|
| events* | number[] | Event timestamps (ms). |
| window | number | The visible recent window in ms (default 60000). |
| now | number | Explicit clock — defaults to the latest event (SSR-safe). |
| label | "count" | "none" | Event-count numeral at the right. |
Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.
Related charts
BreathingDot
React breathing dot chart — ambient load as pulse rate, with a static reduced-motion twin. Tiny accessible SVG microchart; RSC-safe static or /interactive.
CometTrail
React comet trail chart — a path with a fading trail of recent positions. Tiny accessible SVG microchart; RSC-safe static or /interactive.