Headless / Numeric

Progress

Progress reports how far along a task is. It is an output, not an input — it reflects a value your code owns, and the user never interacts with it.

Loading playground…
Updated 3 days agoEdit on GitHubWeb & native

What it is

A single component rendering a role="progressbar". Pass a number for the determinate state — a known fraction of max, with the fill sized to match and aria-valuenow set. Pass null for the indeterminate state — work is happening but its extent is unknown; the fill animates and aria-valuenow is omitted.

Install

example.tsx
yarn add @usemotif/headless
example.tsx
import { Progress } from '@usemotif/headless';

API

Progress

stable
function Progress(props: ProgressProps): JSX.Element
valuenumber | nullrequired

The completed amount, 0 to `max`. Pass `null` for the indeterminate state.

maxnumber= 100

The value representing completion.

aria-label / aria-labelledbystring

Names the progress bar — what it is tracking.

styleCSSProperties

Inline style for the track.

fillStyleCSSProperties

Inline style for the filled portion.

Accessibility

Progress is a role="progressbar". In the determinate state it carries aria-valuenow, aria-valuemin, and aria-valuemax, so a screen reader can announce "60%". In the indeterminate state aria-valuenow is deliberately absent — the correct ARIA for "in progress, amount unknown". Give the bar a name so the user knows what is progressing.

For a progress value that changes as the result of a user action — a file upload, a save — pair Progress with a LiveRegion so milestones are announced. Progress itself only carries the value; it does not announce changes.

Examples

A determinate upload bar:

example.tsx
<Progress
aria-label="Upload progress"
value={uploadedBytes}
max={totalBytes}
style={{ height: 8, background: 'var(--colors-line-base)', borderRadius: 999 }}
fillStyle={{ height: '100%', background: 'var(--colors-status-success)', borderRadius: 999 }}
/>

An indeterminate spinner-bar:

example.tsx
<Progress aria-label="Loading" value={loading ? null : 100} />