"use client";
import * as React from "react";
import { Progress } from "@/components/ui/progress";
export function ProgressDemo() {
const [progress, setProgress] = React.useState(13);
React.useEffect(() => {
const timer = setTimeout(() => setProgress(66), 500);
return () => clearTimeout(timer);
}, []);
return <Progress value={progress} className="w-[60%]" />;
}
pnpm dlx shadcn@latest add https://herocn.dev/r/progress.jsonimport {
Progress,
ProgressLabel,
ProgressValue,
} from "@/components/ui/progress"<Progress value={33} />Use ProgressLabel and ProgressValue as optional children for a header row; the track and indicator are rendered automatically inside Progress.
Progress
├── ProgressLabel
├── ProgressValue
└── ProgressTrack
└── ProgressIndicatorUse ProgressLabel and ProgressValue to show a title and formatted percentage.
import {
Progress,
ProgressLabel,
ProgressValue,
} from "@/components/ui/progress";
export function ProgressLabelExample() {
return (
<Progress value={56} className="w-full max-w-sm">
<ProgressLabel>Upload progress</ProgressLabel>
<ProgressValue />
</Progress>
);
}
Use the variant prop to match semantic status colors (default uses the neutral foreground on the track).
Default
Primary
Success
Warning
Destructive
import { Progress } from "@/components/ui/progress";
export function ProgressVariants() {
return (
<div className="flex w-full max-w-sm flex-col gap-4">
<div className="space-y-1">
<p className="text-muted-foreground text-xs">Default</p>
<Progress value={45} variant="default" className="w-full" />
</div>
<div className="space-y-1">
<p className="text-muted-foreground text-xs">Primary</p>
<Progress value={50} variant="primary" className="w-full" />
</div>
<div className="space-y-1">
<p className="text-muted-foreground text-xs">Success</p>
<Progress value={72} variant="success" className="w-full" />
</div>
<div className="space-y-1">
<p className="text-muted-foreground text-xs">Warning</p>
<Progress value={38} variant="warning" className="w-full" />
</div>
<div className="space-y-1">
<p className="text-muted-foreground text-xs">Destructive</p>
<Progress value={88} variant="destructive" className="w-full" />
</div>
</div>
);
}
Use the size prop to change the track height (sm, default, or lg).
import {
Progress,
ProgressLabel,
ProgressValue,
} from "@/components/ui/progress";
export function ProgressSizes() {
return (
<div className="flex w-full max-w-sm flex-col gap-6">
<Progress value={40} size="sm" className="w-full">
<ProgressLabel>Small</ProgressLabel>
<ProgressValue />
</Progress>
<Progress value={55} size="default" className="w-full">
<ProgressLabel>Default</ProgressLabel>
<ProgressValue />
</Progress>
<Progress value={70} size="lg" className="w-full">
<ProgressLabel>Large</ProgressLabel>
<ProgressValue />
</Progress>
</div>
);
}
Drive the bar from another control, such as a slider.
"use client";
import * as React from "react";
import { Progress } from "@/registry/new-york-v4//ui/progress";
import { Slider } from "@/registry/new-york-v4//ui/slider";
export function ProgressControlled() {
const [value, setValue] = React.useState(50);
return (
<div className="flex w-full max-w-sm flex-col gap-4">
<Progress size="lg" value={value} className="w-full" />
<Slider
value={value}
onValueChange={(value) => setValue(value as number)}
min={0}
max={100}
step={1}
/>
</div>
);
}
To enable RTL support in shadcn/ui, see the RTL configuration guide.
"use client";
import {
type Translations,
useTranslation,
} from "@/components/language-selector";
import {
Progress,
ProgressLabel,
ProgressValue,
} from "@/components/ui/progress";
const translations: Translations = {
en: {
dir: "ltr",
values: {
label: "Upload progress",
},
},
ar: {
dir: "rtl",
values: {
label: "تقدم الرفع",
},
},
he: {
dir: "rtl",
values: {
label: "התקדמות העלאה",
},
},
};
function toArabicNumerals(num: number): string {
const arabicNumerals = ["٠", "١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩"];
return num
.toString()
.split("")
.map((digit) => arabicNumerals[Number.parseInt(digit, 10)] ?? digit)
.join("");
}
export function ProgressRtl() {
const { dir, t, language } = useTranslation(translations, "ar");
const formatNumber = (num: number): string => {
if (language === "ar") {
return toArabicNumerals(num);
}
return num.toString();
};
return (
<div className="w-full max-w-sm" lang={language} dir={dir}>
<Progress value={56} className="w-full">
<ProgressLabel>{t.label}</ProgressLabel>
<ProgressValue>
{(_formatted, value) => (
<span className="ms-auto">{formatNumber(value ?? 0)}%</span>
)}
</ProgressValue>
</Progress>
</div>
);
}
Progress wraps Base UI Progress.Root and sets variant and size on the root for styling the built-in track and indicator.
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "primary" | "success" | "warning" | "destructive" | "primary" |
| size | "sm" | "default" | "lg" | "default" |
| value | number | null | — |
| min | number | 0 |
| max | number | 100 |
| format | Intl.NumberFormatOptions | — |
| className | string | — |
| children | React.ReactNode | — |
| Prop | Type | Default |
|---|---|---|
| className | string | — |
| Prop | Type | Default |
|---|---|---|
| className | string | — |
| children | (formattedValue: string | null, value: number | null) => React.ReactNode | — |
These parts are rendered by default. They are exported if you need to build a fully custom layout alongside the same primitives.
For the full root API (locale, getAriaValueText, etc.), see the Base UI Progress documentation.