Skip to content
microcharts
ReferenceExpressiveHeartbeatBlip

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.

HeartbeatBlip
interactive · 3.57 kB · static · 2.18 kB

Install

Import & usereact/heartbeat-blip
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;
Needs package + stylesheetSet up with AI

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

count label, and the flatline down-signal
locale

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

empty — the flat down-signal
non-finite timestamps are dropped, not drawn as spikes
events outside the window are dropped, not clamped

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

PropTypeNotes
events*number[]Event timestamps (ms).
windownumberThe visible recent window in ms (default 60000).
nownumberExplicit 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