Browse the docs
ComponentsNo. 41 of 109
Progress
A progress bar for tasks with a known amount, or an indeterminate one when the amount is unknown.
Installation
pnpm dlx shadcn@latest add @corsair-ui/progressNo setup needed: the CLI finds @corsair-ui in the shadcn registry directory. The files land in your project as source: edit them like your own code. More in Installation.
- Also adds
- utils
- npm
@radix-ui/react-progressclsxtailwind-merge
Examples
Determinate
Driven by the slider; named by its visible label.
60%
Indeterminate
value={null} when the amount is unknown.
Loading charts
API
| Prop | Type | Default |
|---|---|---|
value0 to max, clamped; null means the amount is unknown. | number | null | null |
maxThe value that means done. | number | 100 |
getValueLabelWhat screen readers read out; a percentage by default. | (value, max) => string | — |
Built on Radix Progress; every prop it takes is passed through.
Accessibility
- A progressbar with aria-valuenow and aria-valuemax. Name it with aria-label or aria-labelledby.
- Indeterminate bars slide a segment across; with prefers-reduced-motion they hold still at half opacity.
Compatibility
- Tailwind CSS
- 3.4 and 4, both checked in CI
- React
- 19
- Rendering
- Client component ("use client")
Source
1 file, installed as source you own
progress.tsxView on GitHub"use client";
import * as ProgressPrimitive from "@radix-ui/react-progress";
import type { ComponentProps } from "react";
import { cn } from "@/registry/default/lib/utils";
interface ProgressProps extends ComponentProps<typeof ProgressPrimitive.Root> {
/** How far along the task is, from 0 to `max`. `null` (the default) means the amount is unknown. */
value?: number | null;
/** The value that means done. */
max?: number;
}
/**
* A bar that shows how far a task has got: uploads, installs, multi-step
* forms. It is a `progressbar` with `aria-valuenow` / `aria-valuemax`, and
* `getValueLabel` sets the text screen readers read out (a percentage by
* default). Give it a name with `aria-label` or `aria-labelledby` pointing
* at a visible label. Pass `value={null}` when the amount is unknown: the
* bar becomes indeterminate (`data-state="indeterminate"`) and a short
* segment slides across it; with reduced motion it shows a still,
* half-opacity bar instead. Values outside 0 to `max` are clamped.
*
* @example
* <Progress value={uploaded} max={total} aria-label="Uploading photos" />
* <Progress value={null} aria-label="Loading results" />
*/
function Progress({ className, value = null, max = 100, ...props }: ProgressProps) {
const limit = max > 0 ? max : 100;
const clamped = value === null ? null : Math.min(limit, Math.max(0, value));
const percent = clamped === null ? 0 : (clamped / limit) * 100;
return (
<ProgressPrimitive.Root
data-slot="progress"
value={clamped}
max={limit}
className={cn("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full", className)}
{...props}
>
<ProgressPrimitive.Indicator
data-slot="progress-indicator"
className={cn(
"bg-primary h-full w-full flex-1 transition-transform motion-reduce:transition-none",
// Unknown amount: a 40% segment slides across. With reduced motion the
// whole bar stays put at half opacity instead.
"motion-safe:data-[state=indeterminate]:animate-progress-indeterminate motion-safe:data-[state=indeterminate]:w-2/5",
"motion-reduce:data-[state=indeterminate]:opacity-50"
)}
style={clamped === null ? undefined : { transform: `translateX(-${100 - percent}%)` }}
/>
</ProgressPrimitive.Root>
);
}
export { Progress, type ProgressProps };