EventTimeline
EventTimeline places instants as diamonds and durations as rects on one compact row.
- encodes
- span extent on a linear time axis; points as position marks
- precision
- high
- nodes
- ≤ 14 (1 per item + track + now tick, ≤ 12 items documented)
An API had a freeze window overnight, nine healthy hours, an incident at 11:00, and a release at 20:00. EventTimeline puts all of it on one row: diamonds mark instants, rects mark durations. The type distinction is a shape, so it survives 12 px where color coding wouldn't. Duration is length on a linear time axis, never log or compressed time.
Install
import { EventTimeline } from "@microcharts/react/event-timeline";// windows, today — real values under “Sample data” below<EventTimeline data={windows} domain={today} title="API uptime" />Sample data
const windows = [ { start: Date.UTC(2026, 5, 3, 1), end: Date.UTC(2026, 5, 3, 5), label: "Freeze", kind: "accent" }, { start: Date.UTC(2026, 5, 3, 6), end: Date.UTC(2026, 5, 3, 15), label: "Healthy", kind: "positive" }, { start: Date.UTC(2026, 5, 3, 11), label: "Incident", kind: "negative" }, { start: Date.UTC(2026, 5, 3, 16), end: Date.UTC(2026, 5, 3, 18), kind: "negative" }, { start: Date.UTC(2026, 5, 3, 20), label: "Release" },];const today: [number, number] = [Date.UTC(2026, 5, 3), Date.UTC(2026, 5, 4)];Try it
When to use it
Use it for per-service uptime rows, and for on-call shifts and release windows in cards. Avoid it past roughly 12 items; for aggregated durations use a MiniBar of the totals.
Sizing
Variants
Edge cases
A span that crosses the domain boundary is cut flat at the edge rather than rescaled to fit, so you see the part inside the window and nothing more. An item that falls entirely outside the domain is excluded, with a dev-only console warning. Spans that overlap render translucent in data order; that translucency is a legibility device rather than an encoding, and the exact intervals are in the announcements.
Four homes
Accessibility
The accessible name is the coverage read: "2 spans covering 46% of the window; 0 events." Coverage merges intervals first, so overlaps never double-count. The interactive entry cycles items chronologically: spans announce "Freeze: Jun 3, 01:00 to Jun 3, 05:00 — 4h.", instants "Incident: Jun 3, 11:00."
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 or press Escape.
On touch, a tap pins and a drag scrubs.
Props
| Prop | Type | Notes |
|---|---|---|
| data* | { start; end?; label?; kind? }[] | Spans (with end) and point events (without), ms epoch or Date. |
| domain | [start, end] | The window — fix it across rows for small multiples. |
| now | number | Date | Current-moment tick; authored, never implicit. |
| label | "none" | "spans" | Centered in-span labels with deterministic drop-out. |
| dateFormat | Intl.DateTimeFormatOptions | (d: Date) => string | interactiveAnnounced timestamp format for focused events (defaults to a locale date-time). |
| animate | boolean | interactiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion. |
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.