Skip to content

Progress ring

Shows how much of a task is done as a small ring that fills from 0 to 100. For a value against a target, use Gauge.

Installation

pnpm dlx shadcn@latest add @wisty/progress-ring

The CLI installs the Radix or Base UI file to match your project.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • A task is partly done and you know how much, like a profile's completion, an import's progress or a setup step, and there's room for a small ring but not a bar.
  • Time or a quota runs down, like the days left to respond to a case. Use direction="drain".
  • The share needs a status beside it: the step people are on (active-step), running late (warning), out of time (expired) or done (complete).
  • It replaces lightning-progress-ring.

Don't use it

  • You don't know how long the work will take. Use Spinner.
  • There's room for a bar, or the progress is the main thing on the page, like an upload. Use Progress.
  • A value is measured against a target, with ranges like behind and on track, like quota attainment. Use Gauge.
  • People work through named steps in order. Use Progress indicator.
  • You want to show the percent. Write it beside the ring, never in it. For a large dial with its value inside, use Gauge.

Usage

import { ProgressRing } from "@/components/wisty/progress-ring"
<ProgressRing value={40} aria-label="Data import" />
  • value is a percent, from 0 to 100. Pass null when it isn't known yet, so the ring stays empty and screen readers hear no value.
  • Children go inside the ring: a step number, as a setup list numbers its steps. The warning, expired and complete variants show their icon there instead.
  • With variant="base-autocomplete", the ring turns complete, with a check, when value reaches 100.

Examples

Variants

Base, active step, warning and expired at 60, then complete at 100, with the warning, expired and complete icons each carrying a hidden word.

Warning
Expired
Complete

Step number

A Setup Assistant step: a large `active-step` ring a third done, with the step number "2" as its children.

2

Countdown

With `direction="drain"`, the ring empties counterclockwise as time runs out, named by the text beside it through `aria-labelledby`.

3 days left

Sizes

The default medium ring and `size="large"`, both at 40.

Completes at 100

With `variant="base-autocomplete"`, rings at 0 and 50 stay base, and the one at 100 turns complete with a check.

Complete

API reference

ProgressRing

PropTypeDefaultDescription
aria-labelstringNames what the ring measures, like "Upload progress".
aria-labelledbystringThe id of visible text that names what the ring measures.
childrenReact.ReactNodeA step number or one glyph inside the ring. Not shown with an icon.
variant"base" | "base-autocomplete" | "active-step" | "warning" | "expired" | "complete""base"
size"medium" | "large""medium"
value*number | nullHow much is done, from 0 to 100. Clamped. null when it isn't known.
direction"fill" | "drain""fill"fill grows the ring clockwise; drain empties it counterclockwise.
iconLabelstringReplaces the icon's hidden word ("Warning", "Expired", "Complete").

Also takes every <div> (opens in a new tab) prop.

SLDS mapping

ProgressRing is slds-progress-ring. The progressbar inside it is slds-progress-ring__progress, holding the arc, slds-progress-ring__path. Children and the variant's icon go in slds-progress-ring__content, and the dot at the arc's end is slds-progress-ring__progress-head.

Known gaps:

  • The warning, expired and complete icons are Lucide's triangle alert, ban and check, standing for utility:warning, utility:error and utility:check.
  • The blueprint's warning and expired colors without their icon aren't a variant. Like lightning-progress-ring, those variants always show it.
PropValueSLDSNotes
variantbase (added)slds-progress-ringDefault.
variantbase-autocomplete (added)slds-progress-ring, slds-progress-ring_complete at 100Base until the value reaches 100, then complete with a check, as lightning-progress-ring's variant.
variantactive-step (added)slds-progress-ring_active-stepThe step people are on, in the brand color.
variantwarning (added)slds-progress-ring_warningA warning icon inside the ring (utility:warning).
variantexpired (added)slds-progress-ring_expiredAn error icon inside the ring (utility:error).
variantcomplete (added)slds-progress-ring_completeA check on a filled circle (utility:check).
sizemedium (added)slds-progress-ringDefault.
sizelarge (added)slds-progress-ring_large
directionfill (added)slds-progress-ring__path, swept clockwiseDefault.
directiondrain (added)slds-progress-ring__path, swept counterclockwise

Accessibility

  • Name the ring with aria-label, or point aria-labelledby at visible text beside it. Say what it measures, like "Upload progress".
  • The value is read as a percent of 100. To show a count, like 3 of 7 days, pass its percent and write the count beside the ring.
  • The warning, expired and complete icons are read as a hidden word: "Warning", "Expired" or "Complete". Translate it with iconLabel.
  • Children are read after the progressbar, not as its value. A bare "2" says little, so put the step in the label too, like "Step 2 of 4".
  • The ring is never focusable. Don't put a link or button inside it.

Motion

  • The arc doesn't animate. A new value jumps into place, in every theme.

Rules

  • Keep value a percent. Convert a count or an amount first, like Math.round((done / total) * 100).
  • Use variant="complete" only at 100. To turn complete when the task finishes, use base-autocomplete.
  • Children are a step number or one glyph, never a percentage or a word. The ring is sized for one or two characters.
  • Never pass asChild or render.