Progress
A bar showing a value toward a total, on a tinted track, in two sizes.
Progress shows how far along one task is, like an upload or an import, as a fill that grows
across a track. It wraps Radix Progress, takes a value out of max, 100 by default, and
exposes it as a progressbar. The fill is bg-tone, the primary unless you set a tone, on a track that is an 8% wash of the same color.
Usage
import { Progress } from '@/components/ui/progress';
<Progress value={uploaded} aria-label="Upload" />Fill and motion
The fill is sized by its width, as a percentage of the track, not by a transform. When
value changes it eases to the new width, and under reduced motion it jumps. Click Start
to run it.
A value that is missing or not a number draws an empty fill, and one out of range is logged by Radix as an error. Keep it between 0 and 100, and see Accessibility for what is exposed when there is no value.
Two sizes
md is the default, a 16px track with the fill inset 4px. sm is an 8px track with the fill
inset 2px, the same proportions at half the size, for a thin bar in a table row or under a
stat. Use size rather than a height class, since the inset is part of the size.
<Progress value={41} size="sm" aria-label="Sent" />Props
Progress takes the props of Radix Progress's root, plus this.
| Prop | Type | Default | What it does |
|---|---|---|---|
tone | "neutral" | "primary" | "support" | "danger" | "success" | "warning" | "info" | "inherit" | "primary" | The hue of the fill and its track, such as success for a finished upload. inherit takes the tone of the surrounding scope. |
size | "sm" | "md" | "md" | An 8px or a 16px track, with the fill inset by a quarter of the height. |
value | number | null | How far along it is, out of max. Sets aria-valuenow and the fill width. null is indeterminate. | |
max | number | 100 | The value that fills the track. |
Accessibility
- The root has
role="progressbar", witharia-valuemin="0",aria-valuemax, andaria-valuenowset tovalue. The defaultaria-valuetextis the rounded percent, like "30%", andgetValueLabelchanges it. - A bar has no name of its own, so give every one an
aria-label, or pointaria-labelledbyat a visible label, so the value is announced with a name. - A
nullor missing value makes Radix droparia-valuenowand setdata-state="indeterminate", which is how a screen reader hears an unknown amount. Progress sweeps a segment across the track for that state, and under reduced motion it holds still as a tint across the whole track, so it does not read as a fixed amount. - Changes are not announced as they happen. A screen reader reads the value when the bar is focused or browsed to, so announce the result of the task, like "Import finished", in a live region or a Toaster.
- The bar is not focusable and takes no input.
- Under reduced motion the fill changes without a transition.
- There is no forced-colors rule. The fill and the track are background colors, which the
browser replaces there, so the bar may not draw while
aria-valuenowstill carries the value.
When to use something else
- Segmented bar for a whole split into parts, like delivered, bounced, and not sent. It is a snapshot of a distribution and Progress is one value moving toward a total.
- Skeleton for content that is loading, when the shape is known and the progress is not.
- A Button's
loadingfor an action that started at that button and has no useful percentage. It disables the button and shows a spinner in place. - Stepper for progress through named stages rather than a quantity.
Install
shadcn add @gesso/progress