caliper uidocs

Progress ring

Free

Determinate progress in a ring that eases to each value, draws a check when done and a cross when it fails.

Installs components/ui/progress-ring.tsxDepends on lucide-reactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/progress-ring.json

When to use #

Use it for

  • Uploads, exports and syncs with a known size.
  • Compact progress in a row or on an avatar.

Skip it when

  • Work with no known end; use a spinner.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/progress-ring.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { ProgressRing } from "@/components/ui/progress-ring"

export function ProgressRingDemo() {
  const [value, setValue] = useState(35);
  const [failed, setFailed] = useState(false);
  return (
    <div className="grid justify-items-center gap-4">
      <div className="flex items-center gap-6">
        <ProgressRing value={value} label="Uploading board-pack.pdf" size={56} showValue status={failed ? "error" : value >= 100 ? "done" : "active"} />
        <ProgressRing value={value} label="Uploading board-pack.pdf, small" size={24} thickness={3} status={failed ? "error" : value >= 100 ? "done" : "active"} />
      </div>
      <div className="flex flex-wrap justify-center gap-2">
        <Button size="sm" variant="secondary" onClick={() => { setFailed(false); setValue((v) => Math.min(100, v + 20)); }}>Add 20%</Button>
        <Button size="sm" variant="secondary" onClick={() => setFailed(true)}>Fail</Button>
        <Button size="sm" variant="ghost" onClick={() => { setFailed(false); setValue(0); }}>Reset</Button>
      </div>
    </div>
  );
}

API reference #

ProgressRing

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
value *number—
maxnumber100
label *string—What is progressing, e.g. "Uploading report.pdf".
status"error" | "done" | "active""active"
sizenumber40Outer diameter in pixels.
thicknessnumber4
showValuebooleanfalseShows the percentage in the middle; needs a size of 40 or more.

Accessibility #

It exposes progressbar semantics, with aria-label, aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext and aria-hidden kept in step with state.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.