EtaBar
EtaBar shows time remaining, sized by the observed rate instead of linear interpolation.
- encodes
- time axis — elapsed vs predicted-remaining
- precision
- high / medium
- nodes
- ≤ 4
EtaBar takes a job's progress, elapsed time, and observed rate and shows how long is left at that pace. Its axis
is time rather than fraction: the solid part is the elapsed share of the predicted total, and the muted remainder is how
long the rest will take at the current rate. When the rate drops, the remainder grows instead of marching to a fixed
finish. The ETA label is on by default; label="percent" shows the fraction instead.
Install
import { EtaBar } from "@microcharts/react/eta-bar";<EtaBar progress={0.64} elapsed={3.6} rate={0.18} etaFormat={(t) => `${Math.round(t)} min`} title="Export" />Try it
When to use it
Use it for download and export progress, job-queue ETAs, and deploy timers. For fraction-only progress use Progress; for unbounded counters use Delta.
Sizing
Motion, and reduced motion
The interactive entry eases the elapsed/remainder split (SVG x/width) to its new geometry whenever progress,
elapsed, or rate change. The split only ever moves to a value computed from data you passed in on that render; it
never interpolates toward a frame it hasn't seen. The transition lives in shared CSS (.mc-eta-live rect), so it
inherits the shared :where(.mc-root *) { transition: none } reduced-motion block: under
prefers-reduced-motion: reduce the bar snaps straight to each new split instead of easing. The live region
re-announces the forecast on a throttle (announceEvery, default 10 s) so a fast-updating value doesn't spam a screen
reader.
Variants
Without an etaFormat, the remaining-time figure falls back to format/locale: under de-DE that's a decimal comma
("11,6") rather than the English decimal point ("11.6"). Pass etaFormat, as the hero example does, to add a unit. It
always receives the raw number, so it can format the unit itself in whatever locale-aware way you need.
Edge cases
An explicit rate of 0, or an absent rate from which no positive average can be derived from elapsed, renders the
remainder as a diagonal-hatched texture instead of a solid bar. That texture means unknown, and the summary says
"stalled" rather than naming a duration. When the predicted remainder is far larger than the elapsed time, the done
segment would round to an unreadable sliver, so the geometry clamps it to a visible 10% and adds a chevron marking the
clamp as approximate. At progress >= 1 the bar fills completely regardless of rate.
Four homes
Accessibility
The accessible name is the forecast: "42% done; about 11.6 remaining at the current rate." A stalled transfer reads "30% done; stalled." and a finished one "Done." The interactive entry re-announces on a throttle as the forecast changes.
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.
Hover or focus also reveals the reading itself in a floating chip, for the sizes and label modes where the mark
does not print it; readout={false} drops the chip and keeps everything else.
Props
| Prop | Type | Notes |
|---|---|---|
| progress* | number | Completed fraction 0–1. |
| elapsed* | number | Time spent, any unit. |
| rate | number | Progress per time unit — pass a recent-window rate. |
| label | "eta" | "percent" | "none" | The remaining-time read is the product. |
| etaFormat | (t: number) => string | Unit-bearing ETA label ("2 min") — the caller owns units. |
| announceEvery | number | interactiveMinimum ms between live-region announcements as the ETA streams (default 10000). |
| 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.