Skip to content
microcharts
ReferenceFrontierEtaBar

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.

EtaBar
interactive · 3.74 kB · static · 2.23 kB

Install

Import & usereact/eta-bar
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" />
Needs package + stylesheetSet up with AI

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

percent
locale

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

rate hits zero (stalled)
remainder far exceeds elapsed (overflow)
done

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

PropTypeNotes
progress*numberCompleted fraction 0–1.
elapsed*numberTime spent, any unit.
ratenumberProgress per time unit — pass a recent-window rate.
label"eta" | "percent" | "none"The remaining-time read is the product.
etaFormat(t: number) => stringUnit-bearing ETA label ("2 min") — the caller owns units.
announceEverynumberinteractiveMinimum ms between live-region announcements as the ETA streams (default 10000).
animatebooleaninteractiveOpt-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.

Related charts