What is Progress bar?
Progress bar: A progress bar is a horizontal track that fills to show how much of a task is done, as a measured percentage or an indeterminate animation when unknown.
A progress bar shows how far along something is: an upload, an export, a multi-step signup, the storage used out of a quota, a goal against a target. The filled portion of the track maps directly to a number, which is what makes it more informative than a spinner. There is also an indeterminate form, a bar that animates without a fixed fill, for work whose size is unknown; it is a spinner in a different shape, and the same rules apply to it.
Determinate or indeterminate
- Determinate when you can measure progress: bytes uploaded, records processed, steps completed. Show the percentage or a count as text next to the bar.
- Indeterminate when you cannot, but expect the wait to be noticeable. Below a second or so, show nothing or a spinner; for layouts you know, a skeleton screen is better.
- Static meters such as storage used or budget spent are progress bars that do not move. HTML has a separate
meterelement for them, and they want a label with the actual numbers. - Fake progress that crawls to 90 percent and waits is a common trick for page loads. Use it only when the real duration is short and unknowable, and never for uploads, where it lies about the data.
Accessibility
The element has role="progressbar" with aria-valuemin, aria-valuemax and aria-valuenow, and aria-valuetext when a plain number would mislead, such as "Step 2 of 4". An indeterminate bar omits aria-valuenow. It needs an accessible name that says what is progressing, through aria-labelledby pointing at a visible label or aria-label. The native <progress> element provides all of this and is fine when you can style it enough.
- Do not announce every update. Set
aria-busyon the region being loaded and announce completion once through a polite live region. - The fill needs 3:1 contrast against the track, and the track against the page, so the amount is readable without the text.
- Under prefers-reduced-motion, replace an indeterminate sweep with a gentle pulse or a static striped pattern.
How to build one in React
MiniDev's Progress wraps the Base UI progress primitive and renders the label and value alongside the track:
import { Progress, ProgressLabel, ProgressValue } from "@/components/ui/progress"
export function UploadProgress({ percent }: { percent: number }) {
return (
<Progress value={percent} className="max-w-sm">
<ProgressLabel>Uploading report.pdf</ProgressLabel>
<ProgressValue />
</Progress>
)
}
Pass value={null} for an indeterminate state. ProgressCircle draws the same value as a ring, StepProgress shows named steps for a stepper, and QuotaBar and UsageMeter cover storage and billing limits. All are in the feedback category.