Shadcn Progress
Free shadcn/ui progress bar for React — built on Radix. A determinate bar with a value, plus labels, colors, sizes, an indeterminate loading state, an upload simulation, and a circular variant.
Installation
{
"registries": {
"@designrevision": "https://registry.designrevision.com/r/{name}.json"
}
}
Add to your existing components.json. Requires shadcn/ui v2.3+.
Packages
Props
value
= 0
number
The progress percentage (0–100). The indicator fills to this width.
className
= —
string
Style the track — height (h-1 / h-2 / h-4), color, radius.
A Radix progress bar — a track (bg-primary/20, h-2 rounded-full) with an indicator that fills to value via a translateX transform, so changes animate smoothly. Pass value 0–100; omit it (or leave the value undefined) and style a sliding indicator for an indeterminate bar. Restyle height/color with a className.
"use client"
import * as React from "react"
import * as ProgressPrimitive from "@radix-ui/react-progress"
import { cn } from "@/lib/utils"
function Progress({
className,
value,
...props
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
return (
<ProgressPrimitive.Root
data-slot="progress"
className={cn(
"bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
className
)}
{...props}
>
<ProgressPrimitive.Indicator
data-slot="progress-indicator"
className="bg-primary h-full w-full flex-1 transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
)
}
export { Progress }
Examples
Determinate
The canonical progress bar — animates from 0 to its `value` on mount.
Packages
Props
No props documented yet.
"use client"
import * as React from "react"
import { Progress } from "@/components/ui/progress"
export default function ProgressDemo01() {
const [value, setValue] = React.useState(0)
React.useEffect(() => {
const timer = setTimeout(() => setValue(66), 400)
return () => clearTimeout(timer)
}, [])
return (
<div className="w-full max-w-sm">
<Progress value={value} />
</div>
)
}
With label
A progress bar with a label and percentage readout — the storage/usage pattern.
Packages
Props
No props documented yet.
import { Progress } from "@/components/ui/progress"
export default function ProgressLabel01() {
const value = 72
return (
<div className="w-full max-w-sm space-y-2">
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">Storage used</span>
<span className="font-medium">{value}%</span>
</div>
<Progress value={value} />
</div>
)
}
Colors
Recolor the indicator with a child selector — success, warning, destructive.
Packages
Props
No props documented yet.
import { Progress } from "@/components/ui/progress"
export default function ProgressColors01() {
return (
<div className="w-full max-w-sm space-y-4">
<Progress
value={80}
className="[&>[data-slot=progress-indicator]]:bg-green-500"
/>
<Progress
value={55}
className="[&>[data-slot=progress-indicator]]:bg-amber-500"
/>
<Progress
value={30}
className="[&>[data-slot=progress-indicator]]:bg-destructive"
/>
</div>
)
}
Sizes
Three track heights — override with `h-1` / `h-2` / `h-4`.
Packages
Props
No props documented yet.
import { Progress } from "@/components/ui/progress"
export default function ProgressSizes01() {
return (
<div className="w-full max-w-sm space-y-4">
<Progress value={60} className="h-1" />
<Progress value={60} className="h-2" />
<Progress value={60} className="h-4" />
</div>
)
}
Indeterminate
A sliding loading bar for unknown duration, via a small keyframe.
Packages
Props
No props documented yet.
// An indeterminate progress bar — for when you can't measure percent complete.
// The sliding indicator uses the .animate-progress-indeterminate keyframe from globals.css.
export default function ProgressIndeterminate01() {
return (
<div className="w-full max-w-sm">
<div className="bg-primary/20 relative h-2 w-full overflow-hidden rounded-full">
<div className="animate-progress-indeterminate bg-primary absolute inset-y-0 left-0 w-1/3 rounded-full" />
</div>
</div>
)
}
Upload simulation
A live bar driven by state — climbs from 0 to 100% on an interval.
Packages
Props
No props documented yet.
"use client"
import * as React from "react"
import { Progress } from "@/components/ui/progress"
export default function ProgressUpload01() {
const [value, setValue] = React.useState(0)
React.useEffect(() => {
const interval = setInterval(() => {
setValue((v) => (v >= 100 ? 0 : v + 5))
}, 300)
return () => clearInterval(interval)
}, [])
return (
<div className="w-full max-w-sm space-y-2">
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">document.pdf</span>
<span className="font-medium">{value}%</span>
</div>
<Progress value={value} />
</div>
)
}
Circular progress
A circular ring built with SVG `stroke-dashoffset`, with a centered percentage.
Packages
Props
No props documented yet.
// A circular progress ring built with SVG stroke-dashoffset — beyond the linear Progress bar.
export default function ProgressCircular01() {
const value = 68
const size = 96
const stroke = 8
const radius = (size - stroke) / 2
const circumference = 2 * Math.PI * radius
const offset = circumference - (value / 100) * circumference
return (
<div className="relative inline-flex items-center justify-center">
<svg width={size} height={size} className="-rotate-90">
<circle
cx={size / 2}
cy={size / 2}
r={radius}
fill="none"
strokeWidth={stroke}
className="stroke-primary/20"
/>
<circle
cx={size / 2}
cy={size / 2}
r={radius}
fill="none"
strokeWidth={stroke}
strokeLinecap="round"
strokeDasharray={circumference}
strokeDashoffset={offset}
className="stroke-primary transition-all"
/>
</svg>
<span className="absolute text-lg font-semibold">{value}%</span>
</div>
)
}
The Shadcn Progress is a Tailwind progress bar built on Radix: a rounded track with an indicator that fills to a value and animates smoothly between updates. The examples below cover the determinate bar, labels, colors, sizes, an indeterminate loading state, a live upload, and a circular variant.
A value-driven bar
The whole API is one prop — value, from 0 to 100:
<Progress value={66} />
The indicator fills via a CSS transform with a transition, so when the value changes the bar animates to the new position. Drive it from state to make it move:
const [value, setValue] = React.useState(0)
React.useEffect(() => {
const t = setTimeout(() => setValue(66), 400)
return () => clearTimeout(t)
}, [])
A label and percentage
Pair the bar with a label row for the storage/usage pattern:
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">Storage used</span>
<span className="font-medium">{value}%</span>
</div>
<Progress value={value} />
Color and size
It's a Tailwind component, so restyle it with a className:
- Color — recolor the indicator with a child selector:
className="[&>[data-slot=progress-indicator]]:bg-green-500". The Colors example shows success / warning / destructive. - Size — override the track height:
h-1,h-2(default), orh-4.
Indeterminate
When you can't measure progress, show a sliding bar instead of a filled value — an absolutely-positioned indicator that animates across the track with a small keyframe:
@keyframes progress-indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(400%); }
}
The Indeterminate example includes the markup and the keyframe.
Live progress
Wire the value to real progress — an upload's onprogress, a multi-step flow, or a timer. The Upload simulation example climbs from 0 to 100% on an interval to show the bar moving.
Circular progress
A ring is a different shape: draw two SVG circles and animate stroke-dashoffset from the full circumference down to the filled fraction. The Circular progress example does this with a centered percentage — handy for compact dashboards.
Accessibility
Radix gives the bar the progressbar role and the right aria-valuenow / aria-valuemax, so assistive tech reads the percentage. For an indeterminate bar, omit the value so it is announced as "loading" rather than a specific number.