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

1 Configure registry — once per project
{
  "registries": {
    "@designrevision": "https://registry.designrevision.com/r/{name}.json"
  }
}

Add to your existing components.json. Requires shadcn/ui v2.3+.

2 Install the component

Packages

@radix-ui/react-progress
utils

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.

Copied!
components/ui/progress.tsx
"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

progress

Props

No props documented yet.

Copied!
components/ui/progress-demo-01.tsx
"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

progress

Props

No props documented yet.

Copied!
components/ui/progress-label-01.tsx
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

progress

Props

No props documented yet.

Copied!
components/ui/progress-colors-01.tsx
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

progress

Props

No props documented yet.

Copied!
components/ui/progress-sizes-01.tsx
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

progress

Props

No props documented yet.

Copied!
components/ui/progress-indeterminate-01.tsx
// 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

progress

Props

No props documented yet.

Copied!
components/ui/progress-upload-01.tsx
"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

progress

Props

No props documented yet.

Copied!
components/ui/progress-circular-01.tsx
// 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>
  )
}

Frequently asked questions

It is a progress bar built on @radix-ui/react-progress — a rounded track with an indicator that fills to a value from 0 to 100. The indicator moves with a CSS transform and a transition, so updates animate smoothly. It is accessible by default (Radix sets the progressbar role and aria values).
Pass value as a number from 0 to 100: . Drive it from state to make it move — e.g. setValue on an interval, or update it as an upload reports progress. The bar transitions to each new value automatically.
This is one — the component is a Tailwind-styled Radix progress bar. The track is bg-primary/20 h-2 rounded-full and the indicator is bg-primary with a translateX transform set from the value. Restyle any of it with a className: height (h-1/h-2/h-4), color, or radius.
Recolor the indicator with a child selector on the className: . That targets the inner indicator (data-slot="progress-indicator"). The Colors example shows success, warning, and destructive bars.
When you cannot measure percent complete, show a sliding indicator instead of a filled value: a track with an absolutely-positioned bar that animates across with a small keyframe (animate-progress-indeterminate). The Indeterminate example includes the markup and the keyframe.
A ring is a separate shape — draw two SVG circles and animate stroke-dashoffset from the circumference down to the filled fraction. The Circular progress example does this with a centered percentage. For a full-featured radial component, it is its own composite.
Override the track height in the className: (thin) up to h-4 (chunky). The default is h-2. The indicator fills the track's full height, so the bar just gets thicker or thinner.