Progress
A linear progress indicator built on Radix UI Progress. Supports three sizes, determinate values from 0–100, and an indeterminate pulse animation when no value is provided — suitable for file uploads, multi-step flows, and loading states.
Installation
npm install @designforge/uiUsage
import { Progress } from "@/components/ui/progress"Examples
All Sizes
Three size variants control the height of the track and indicator bar.
import { Progress } from "@/components/ui/progress"
export function ProgressSizes() {
return (
<div className="flex flex-col gap-4 w-full max-w-sm">
<div className="space-y-1">
<p className="text-xs text-muted-foreground">Small (sm)</p>
<Progress value={60} size="sm" />
</div>
<div className="space-y-1">
<p className="text-xs text-muted-foreground">Medium (md) — default</p>
<Progress value={60} size="md" />
</div>
<div className="space-y-1">
<p className="text-xs text-muted-foreground">Large (lg)</p>
<Progress value={60} size="lg" />
</div>
</div>
)
}Loading States
Show common milestone values to illustrate stepped progress.
import { Progress } from "@/components/ui/progress"
export function ProgressLoadingStates() {
const steps = [
{ label: "Starting…", value: 25 },
{ label: "Halfway there", value: 50 },
{ label: "Almost done", value: 75 },
{ label: "Complete", value: 100 },
]
return (
<div className="flex flex-col gap-4 w-full max-w-sm">
{steps.map(({ label, value }) => (
<div key={value} className="space-y-1">
<div className="flex justify-between text-xs text-muted-foreground">
<span>{label}</span>
<span>{value}%</span>
</div>
<Progress value={value} />
</div>
))}
</div>
)
}Indeterminate
Pass value={null} or omit value entirely to trigger the pulse animation, indicating that the duration is unknown.
import { Progress } from "@/components/ui/progress"
export function ProgressIndeterminate() {
return (
<div className="w-full max-w-sm space-y-1">
<p className="text-xs text-muted-foreground">Fetching data…</p>
<Progress value={null} />
</div>
)
}With Label
Pair the bar with an accessible label and a percentage readout.
import { Progress } from "@/components/ui/progress"
export function ProgressWithLabel() {
const value = 68
return (
<div className="w-full max-w-sm space-y-2">
<div className="flex items-center justify-between">
<label className="text-sm font-medium leading-none">
Profile completion
</label>
<span className="text-sm text-muted-foreground">{value}%</span>
</div>
<Progress value={value} aria-label="Profile completion progress" />
</div>
)
}File Upload Progress
Simulate a file upload with an animated value that increments over time.
import { useEffect, useState } from "react"
import { Progress } from "@/components/ui/progress"
export function FileUploadProgress() {
const [progress, setProgress] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
clearInterval(timer)
return 100
}
return prev + 10
})
}, 400)
return () => clearInterval(timer)
}, [])
return (
<div className="w-full max-w-sm space-y-2">
<div className="flex items-center justify-between text-sm">
<span className="font-medium">design-tokens.zip</span>
<span className="text-muted-foreground">
{progress < 100 ? `${progress}%` : "Done"}
</span>
</div>
<Progress value={progress} size="sm" />
</div>
)
}Multi-Step Progress
Break total progress into named steps to communicate where the user is in a workflow.
import { Progress } from "@/components/ui/progress"
const STEPS = ["Account", "Profile", "Preferences", "Review"]
export function MultiStepProgress() {
const currentStep = 2 // 0-indexed
const value = Math.round(((currentStep + 1) / STEPS.length) * 100)
return (
<div className="w-full max-w-sm space-y-3">
<Progress value={value} size="lg" aria-label="Setup progress" />
<div className="flex justify-between">
{STEPS.map((step, i) => (
<span
key={step}
className={`text-xs ${
i <= currentStep
? "text-foreground font-medium"
: "text-muted-foreground"
}`}
>
{step}
</span>
))}
</div>
</div>
)
}API Reference
Progress
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | undefined | — | Current progress from 0 to 100. Pass null or omit to show the indeterminate pulse animation. |
size | "sm" | "md" | "lg" | "md" | Controls the height of the progress track: sm = h-1.5, md = h-2.5, lg = h-4 (Tailwind height classes). |
className | string | — | Additional CSS classes applied to the root track element. |
ref | React.Ref<HTMLDivElement> | — | Forwarded ref attached to the root element. |
Accessibility
- Rendered as
role="progressbar"via Radix UI, exposingaria-valuenow,aria-valuemin, andaria-valuemaxautomatically. - When
valueisnullorundefined,aria-valuenowis omitted to correctly communicate the indeterminate state to assistive technologies. - Provide a meaningful
aria-labeloraria-labelledbywhen the visible label is not programmatically associated with the bar element. - Colour alone should not be the sole means of communicating progress state — always pair the bar with a visible text readout or label for users with colour vision deficiencies.