Getting started
Foundations
Integrations
Form19
Display5
Layout4
Navigation4
Overlay8
Feedback4
Motion19
#Installation
npx @dinachi/cli@latest add progress-ring#Usage
import { ProgressRing } from "@/components/ui/progress-ring"<ProgressRing value={64} label="Upload" />
<ProgressRing label="Fetching" />#Examples
#Why a spring
Progress arrives in jumps. A tween restarted on every jump either stutters or has to be cancelled and rebuilt. A spring re-targets from wherever the arc currently is and carries its velocity across, so a fast run of updates reads as one accelerating sweep instead of a series of separate moves.
The number in the middle reads the same spring as the arc, so the two can never disagree. It is the same spring the number ticker uses, so a ring and a count beside each other travel on one clock.
#The indeterminate arc sweeps its own length
A fixed arc spinning at a constant rate is a loading spinner, and it says only "still running". Letting the arc lengthen and shorten under the rotation is what makes it read as work happening rather than as a wheel turning: the sweep and the rotation are deliberately different lengths, so the pattern never lands in the same place twice in a row.
Omit value to get it. A ring that reports a number it is guessing at is worse than one
that admits it does not know.
#The arc repaints
The arc is stroke-dashoffset, which is not a compositor property. At this size that is
fine: one small element repainting a stroke. Forty of them on a page would be a different
conversation, and the alternative is a rotated half-disc mask that animates on transform
but cannot do a rounded cap cleanly.
#When to use it
| Use it for | Not for |
|---|---|
| A single operation the reader is waiting on: an upload, an import, a quota. | A list of rows each with their own progress. Use Progress: a bar packs into a row, and forty repainting strokes do not. |
| Compact places where a bar has nowhere to run. | Anything under about 40px. The number stops fitting before the ring does. |
#Accessibility
- Determinate rings are a
progressbarwitharia-valuenow,aria-valuemin,aria-valuemaxand a spokenaria-valuetext. - Indeterminate rings are a
statuswith no value, because there is none. labelis the accessible name, and defaults to "Progress" or "Loading".- Reduced motion has the arc report the value rather than travel to it, and replaces the endless sweep with a static gap. An animation that repeats forever is the thing the preference most clearly means to stop.
#API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| value | number | — | 0–100. Omit it for the indeterminate arc |
| label | string | "Progress" / "Loading" | Accessible name |
| size | number | 96 | Outer diameter in px |
| thickness | number | — | Stroke width in px. Scales with size unless given |
| showValue | boolean | true | Show the percentage in the middle. Ignored when indeterminate — there is no number to show |