Overlays & Feedback

Circular Progress

An animated percentage ring progress / gauge, supporting gradients, gauge arcs, and a counting reveal.

Shows progress as an SVG ring, with the arc and the central number advancing on a single synchronized animation beat, supporting either a full ring or a gauge.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add circular-progress.en

New project or installation trouble? Start here →

Circular Progress is a React circular progress bar (a progress ring with a percentage) built with SVG and Motion. The arc and the center number are driven by one animated value, with gradient colors, rounded caps and an optional glow; set arc below 360 to turn it into a half-circle or 270-degree gauge.

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

72
160
12
360
Active edited usageDefault props
<CircularProgress/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

When to use

  • Upload, export or batch-job progress where you have an exact percentage
  • A "profile completion" or onboarding checklist indicator
  • Plan usage, storage or quota summary cards
  • KPI attainment on a dashboard
  • Not a good fit: gauges that need ticks, a needle and colored zones, use Gauge Meter

Installation

npx webberui@latest add https://webberui.com/r/circular-progress.en.json

Or, once registries are configured in components.json, install it as @webberui/circular-progress.

Usage

import { CircularProgress } from "@/components/ui/circular-progress";

<CircularProgress value={72} />;

To make a gauge: set arc to less than 360, then use startAngle to align the direction of the opening.

<CircularProgress value={86} arc={270} startAngle={135} />;

Examples

Update with upload progress

When value changes, the arc and number tween from the current value to the new one instead of restarting from 0, so you can feed progress events straight in.

"use client";

import * as React from "react";
import { CircularProgress } from "@/components/ui/circular-progress";

export function UploadProgress({ file }: { file: File }) {
  const [percent, setPercent] = React.useState(0);

  React.useEffect(() => {
    const xhr = new XMLHttpRequest();
    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) setPercent((e.loaded / e.total) * 100);
    };
    xhr.open("POST", "/api/upload");
    xhr.send(file);
    return () => xhr.abort();
  }, [file]);

  return <CircularProgress value={percent} size={120} duration={0.4} decimals={0} />;
}

Custom center content

children replaces the default percentage, which suits non-percentage labels like "3 / 5 steps"; value still needs to be converted to 0–100.

<CircularProgress value={(3 / 5) * 100} size={140} strokeWidth={10}>
  <span className="text-2xl font-semibold">3 / 5</span>
  <span className="text-xs text-neutral-500">Setup steps</span>
</CircularProgress>

Solid color with glow for storage usage

A string color gives a single color, and glow adds a drop-shadow in that same color; trackColor sets the track.

<CircularProgress
  value={82.4}
  decimals={1}
  color="#f97316"
  trackColor="#fed7aa"
  glow
  rounded={false}
/>

Props

PropTypeDefaultDescription
valuenumber—Progress percentage (0 to 100)
sizenumber160Component side length (px)
strokeWidthnumber12Ring stroke width (px)
colorstring | string[]cyan-indigo-magenta gradientA single color string, or an array for a gradient along the arc
trackColorstringlow-opacity currentColorTrack color
arcnumber360Total span of the arc (degrees); anything below 360 makes it a gauge
startAnglenumber-90Start angle (degrees); -90 is straight up
durationnumber1.2Duration of the reveal animation (seconds)
delaynumber0Delay before the reveal starts (seconds)
roundedbooleantrueWhether the end caps are rounded
showValuebooleantrueWhether to show the central percentage number
decimalsnumber0Decimal places of the number
suffixstring"%"Suffix after the number
glowbooleanfalseAdds a soft glow in the same color to the progress arc
childrenReactNode—Custom central content, replacing the default number

Accessibility

  • When the user has "reduce motion" enabled at the system level, it renders at the final value directly, without playing the counting or sweeping animation
  • The svg carries role="img" and an aria-label, announcing the current percentage

FAQ

Does it work with the Next.js App Router?

Yes. The source already has "use client", so import it from a Server Component page. The server and first render output 0, and the animation to the target value runs after mount.

What happens with values above 100 or below 0?

value is clamped to 0–100 and NaN is treated as 0, so the arc and number never overflow.

Can I change the gradient direction?

An array color creates a top-left to bottom-right linearGradient, with stops spread evenly in array order. The direction is not exposed as a prop; edit x1, y1, x2 and y2 in the source if you need another one.

How do screen readers announce it?

The svg has role="img" and an aria-label with the target value plus suffix, for example "72%". With reduced motion enabled, the final value renders immediately with no animation.

What are the dependencies?

motion, clsx, tailwind-merge, and the cn helper in @/lib/utils. Without the CLI, copy the source from the Manual tab.