Progress
StableShows how far a task has come, or that it is underway when its length is unknown.
Anatomy
- 1LabelSays what is in progress. It also names the bar for screen readers.
- 2TrackThe whole task, in a muted tint.
- 3ValueThe percentage, in tabular figures so it doesn't jitter as it changes.
- 4IndicatorThe completed share. It animates between values, and sweeps across the track when the value is unknown.
Installation
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/progress.jsonUsage
import { Progress, ProgressLabel, ProgressValue } from "@/components/ui/progress"<Progress value={64}>
<ProgressLabel>Uploading assets</ProgressLabel>
<ProgressValue />
</Progress>Progress renders the track and indicator for you. Children — typically a label and a value — sit on the line above the bar.
Examples
Sizes
sm (4px) fits inside lists and table cells, default (6px) suits most forms and cards, and lg (10px) is for a progress bar that is the focus of the view.
Indeterminate
Pass value={null} when you know work is happening but not how much remains. A segment sweeps across the track until you switch to a determinate value.
Live updates
The indicator animates between values with the slow duration and standard easing, so frequent updates read as smooth motion rather than jumps.
File uploads
In lists, use the sm size and let the file name be the label. Swap the value for a confirmation when an item finishes, so completed rows stop drawing the eye.
- brand-guidelines.pdfDone4.2 MB
- hero-illustration.png2.8 MB
- product-walkthrough.mp4148 MB
Custom value
ProgressValue accepts a render function for units other than percent. When you change the visible format, give assistive technology the same words with getAriaValueText.
<Progress
value={7.2}
max={10}
getAriaValueText={() => "7.2 of 10 gigabytes used"}
>
<ProgressLabel>Storage</ProgressLabel>
<ProgressValue>{() => "7.2 GB of 10 GB"}</ProgressValue>
</Progress>Guidelines
When to use
- For tasks that take longer than about a second and report measurable progress: uploads, exports, imports, builds.
- For quantities against a limit — storage, seats, quota — where the bar is a gauge rather than a timer.
When not to use
- For short or unmeasurable waits inside a control — use a Spinner or the Button's
loadingstate. - For content that is still loading — use a Skeleton that previews the layout.
- For steps in a flow. A progress bar measures work, not navigation.
Skeleton, spinner or progress?
Be honest
Never let a determinate bar stall at 99% or jump backward. If you can't estimate progress reliably, use the indeterminate state and say what's happening in the label.
Accessibility
- Renders
role="progressbar"witharia-valuemin,aria-valuemaxandaria-valuenow. The indeterminate state omitsaria-valuenow, which screen readers announce as busy. ProgressLabelis wired to the bar witharia-labelledby. When you can't show a label, passaria-labeltoProgressinstead — a progress bar must always have a name.aria-valuetextis generated from the formatted value. Override it withgetAriaValueTextwhenever the visible value isn't a percentage.- Progress bars aren't live regions: screen readers read the value when people navigate to it, not on every change. Announce milestones — Upload complete — with a Toast or a
role="status"message. - Under
prefers-reduced-motion, value changes and the indeterminate sweep stop animating.
API reference
Progress
The root. Renders a <div> containing your children followed by ProgressTrack and ProgressIndicator. Accepts every prop of Base UI's Progress.Root.
ProgressLabel
The accessible name of the bar. Renders a <span>.
ProgressValue
Displays the formatted value, right-aligned. Renders a <span>.
ProgressTrack and ProgressIndicator
Rendered by Progress automatically. They are exported for custom compositions — for example, a segmented meter that renders several indicators.