Progress Heartbeat

Tells a working job from a stalled one using its heartbeat, so nobody waits on a dead job or kills a live one.

StopsKilled a job that was still running

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/progress-heartbeat.json

The files land in your project. They're yours to edit.

Usage

progress-heartbeat-usage.tsx
import { ProgressHeartbeat } from "@/components/ui/progress-heartbeat"

<ProgressHeartbeat
  label="Exporting 12,480 rows to CSV"
  value={job.value}
  done={job.done}
  startedAt={job.startedAt}
  lastProgressAt={job.lastProgressAt}
  onRetry={retry}
  onCancel={() => setJob((j) => ({ ...j, cancelled: true }))}
/>

What it catches

  • No heartbeat for stallAfter seconds
  • Alive but not advancing
  • Jumpy countdowns: a widening time-left range instead
  • 100% shown before the job finishes: held at 99%

Props

ProgressHeartbeat props
PropTypeDefault
label* (required)

What is running, e.g. "Exporting 12,480 rows".

string—
value* (required)

Work done so far, 0 to `max`.

number—
max

Total work.

number100
done

True only once the operation has really finished; the bar never shows 100% before.

booleanfalse
heartbeat

Change this whenever the job proves it's alive without advancing (a ping, a log line).

number | string—
lastProgressAt

When the job last advanced (ms), e.g. from the server after a reload.

number—
startedAt

When the job started (ms). Enables "Done in 3m 12s"; without it the component can't know.

number—
stallAfter

Seconds without any heartbeat before the job counts as stalled.

number30
onRetry

Shows a Retry action while stalled.

() => void—
onCancel

Shows a Cancel action while stalled.

() => void—
stalledText

Templates: `{since}` and `{eta}` are filled in.

string"No progress for {since}"
slowText

Shown when the job is alive but not advancing.

string"Still working · no progress for {since}"
estimatingText

Shown while there's too little data for a time-left range.

string"Estimating time left…"
finishingText

Shown while holding at 99%.

string"Finishing up…"
doneText

Shown when the job reports done.

string"Done"
retryLabel

Retry button text.

string"Retry"
cancelLabel

Cancel button text.

string"Cancel"

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • role=progressbar with min, max, now and aria-valuetext including the status
  • Status is text with icons aria-hidden
  • Bar motion is reduced under reduced motion

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Needs heartbeat and progress timestamps from your job
  • Time-left range is an estimate from recent samples
  • Retry and cancel are your callbacks

What it stops

Killed a job that was still running

Also known as

progress bar · loading · job status · upload · export · stalled · heartbeat · eta

Field type

Dependencies

Pairs well with