Progress Bar
Visual progress indicator with labels and completion state.
Progress3 / 10 items
Complete!10 / 10
Installation
pnpm dlx shadcn@latest add https://ui.vllnt.com/r/progress-bar.jsonbash
Code
import { cn } from "../../lib/utils";
export type ProgressBarProps = {
className?: string;
completedLabel?: string;
currentLabel?: string;
isComplete?: boolean;
isLoading?: boolean;
max: number;
showLabels?: boolean;
value: number;
};
export function ProgressBar({
className,
completedLabel,
currentLabel,
isComplete = false,
isLoading = false,
max,
showLabels = true,
value,
}: ProgressBarProps): React.ReactNode {
const percent = max > 0 ? Math.round((value / max) * 100) : 0;
return (
<div
className={cn(
"border rounded-lg p-4 transition-colors",
isLoading && "border-border bg-muted/30",
!isLoading && isComplete && "border-green-500/50 bg-green-500/5",
!isLoading && !isComplete && "border-border bg-muted/30",
className,
)}
>
{showLabels ? (
<div className="flex items-center justify-between mb-2 h-5">
{isLoading ? (
<>
<span className="h-5 w-16 bg-muted rounded animate-pulse" />
<span className="h-5 w-24 bg-muted rounded animate-pulse" />
</>
) : (
<>
<span className="text-sm font-medium leading-5">
{currentLabel ?? (isComplete ? "Complete" : "Progress")}
</span>
<span className="text-sm text-muted-foreground leading-5">
{value} / {max} {completedLabel ?? ""}
</span>
</>
)}
</div>
) : null}
<div className="h-2 bg-muted rounded-full overflow-hidden">
<div
className={cn(
"h-full transition-all duration-300",
isLoading && "w-0",
!isLoading && isComplete && "bg-green-500",
!isLoading && !isComplete && "bg-primary",
)}
style={isLoading ? undefined : { width: `${percent}%` }}
/>
</div>
</div>
);
}
typescript