Skip to content
microcharts
ReferenceExpressiveCometTrail

CometTrail

CometTrail marks the current value with a bright head dot and fades the recent positions behind it by age.

encodes
head position (now) + opacity-fading positional trail
precision
medium
nodes
trail + 2
collection
Expressive
Source

CometTrail plots the current value as a bright head dot, with recent points trailing behind it and fading with age. In the interactive entry the head eases to each new value and the old head steps down into the trail, so a live stream draws a comet and a stall goes still. Opacity encodes age only; the y position carries the value. The trail is recency context, not data you have to decode.

CometTrail
interactive · 5.15 kB · static · 2.47 kB

Install

Import & usereact/comet-trail
import { CometTrail } from "@microcharts/react/comet-trail";// rollingWindow — real values under “Sample data” below<CometTrail data={rollingWindow} title="Latency" />
Sample data
const rollingWindow = [40, 45, 50, 55, 60, 65, 70, 72, 75, 78, 80, 84, 87];
Needs package + stylesheetSet up with AI

Try it

Live playground
onActive
idle
  1. hover · rove · click…

A live rolling value. The bright head is now; the fading trail is where it has just been (opacity is age, never value). Each update eases the head to the new position — a steady stream makes the comet, a stall goes still. Reduced-motion readers get the same decaying dot-sparkline, repositioned instantly.

trail · 12
label
readout
import { CometTrail } from "@microcharts/react/comet-trail/interactive";// rollingWindow — real values under “Sample data” below<CometTrail  data={rollingWindow}/>
Sample data
const rollingWindow = [40, 45, 50, 55, 60, 65, 70, 72, 75, 78, 80, 84, 87];

Motion, and reduced motion

Motion happens only on a data change; there is no idle loop. When a new value arrives the head eases (~200 ms, the library's canonical strong ease-out) from its old position to the new one, and the previous head steps down into the trail. A continuous stream produces the comet; a stalled stream goes still, which is itself the signal. The head never simulates phantom positions between updates: it eases to each real value. Under prefers-reduced-motion the head repositions instantly, since the static encoding is already complete. The same holds off-screen — one shared viewport check means the ease only runs while the chart is in view.

When to use it

Use it for a live price or metric with a little recency context, a realtime KPI that should show momentum, or per-stream "where is it now" in a table. For the full history or an exact multi-point comparison reach for Sparkline (or DotPlot); for discrete events, HeartbeatBlip.

Sizing

width and height are viewBox units that also set the rendered pixel box — they default to 60 × 16, sized to sit in a table cell or beside a line of text. Omit them and drive the width from CSS to fill a column; the viewBox keeps the aspect ratio. trail controls how many recent points the tail carries, which is a density decision rather than a size one. It is capped at 20: past that the trail stops being a trail and becomes a sparkline, which is the better tool for the full history.

Variants

label="last" is on by default, so the head always prints its number. Changing trail gives you more or less recency context without changing the head read, because opacity encodes age and nothing else.

shorter trail, or no numeral

Edge cases

empty
single point
trail longer than the data

Empty data draws just the frame, with "No data." as the summary. A single point has no trail to fade: the head alone, and "Now 52." A trail larger than the data never backfills or pads. It shows every point there is.

Four homes

In a sentence

p95 latency is climbing — now at 87, with the last hour of momentum visible.

In a table cell
checkout-api87 ms
auth-api19 ms
search-api85 ms
In a KPI card
API latency
87ms, now
In a tab header
checkoutauthsearch

Preview uses the public chart API only. When an interactive twin exists, it swaps in with the same props and no entrance motion - code samples stay on the static import. Placement recipes: Composition, Sizing.

Accessibility

The accessible name is the now-value and the recent trend: "Now 62, rising over the last 3 updates." With a single point it is the value alone, as in the edge case above: "Now 52." Arrow keys step back through the trail ("3 updates ago: 78.") and return toward now. Motion is gated on prefers-reduced-motion, and no reading depends on decoding the fade.

The interactive entry follows the shared interaction contract: arrow keys rove between units on both axes, Home and End jump to the ends, and a click, tap, Enter or Space selects a unit — pinning its readout so it survives blur, until you select it again, press Escape, or press outside the chart. On touch, a tap pins and a drag scrubs.

Props

PropTypeNotes
data*number[]The rolling window, oldest → newest (last = now).
trailnumberPoints kept visible (default 12, cap 20).
label"last" | "none"Numeral after the head (default last).
fontSizenumberType size of the numeral after the head, in viewBox units. Defaults from `height`.

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