GitHub

Circular Progress

Displays an ongoing process or activity in a circular format.

Loading
Loading...
"use client";
 
import { useEffect, useState } from "react";
import { CircularProgress } from "@/components/ui/circular-progress";
import { Slider } from "@/components/ui/slider";
 
export function CircularProgressDemo() {
  const [progress, setProgress] = useState(45);
  const [autoProgress, setAutoProgress] = useState(false);
 
  useEffect(() => {
    if (!autoProgress) {
      return;
    }
 
    const interval = setInterval(() => {
      setProgress((previous) => {
        if (previous >= 100) {
          setAutoProgress(false);
          return 0;
        }
 
        return previous + 5;
      });
    }, 500);
 
    return () => clearInterval(interval);
  }, [autoProgress]);
 
  return (
    <div className="grid w-full max-w-md gap-6">
      <div className="flex items-center justify-center gap-8">
        <div className="flex flex-col items-center">
          <CircularProgress className="size-24" value={progress} />
          <span className="mt-2 text-muted-foreground text-sm">With value</span>
        </div>
 
        <div className="flex flex-col items-center">
          <CircularProgress className="size-24" hideText value={progress} />
          <span className="mt-2 text-muted-foreground text-sm">
            Indicator only
          </span>
        </div>
      </div>
 
      <div className="space-y-4">
        <Slider
          max={100}
          min={0}
          onValueChange={(value) => {
            setProgress(value[0] ?? 0);
          }}
          step={1}
          value={[progress]}
        />
      </div>
    </div>
  );
}

Installation

npx shadcn@latest add "https://ui.blode.co/r/styles/default/circular-progress"

Usage

import { CircularProgress } from "@/components/ui/circular-progress";
<CircularProgress value={75} />

Examples

Basic

Loading
Loading...
"use client";
 
import { useEffect, useState } from "react";
import { CircularProgress } from "@/components/ui/circular-progress";
import { Slider } from "@/components/ui/slider";
 
export function CircularProgressDemo() {
  const [progress, setProgress] = useState(45);
  const [autoProgress, setAutoProgress] = useState(false);
 
  useEffect(() => {
    if (!autoProgress) {
      return;
    }
 
    const interval = setInterval(() => {
      setProgress((previous) => {
        if (previous >= 100) {
          setAutoProgress(false);
          return 0;
        }
 
        return previous + 5;
      });
    }, 500);
 
    return () => clearInterval(interval);
  }, [autoProgress]);
 
  return (
    <div className="grid w-full max-w-md gap-6">
      <div className="flex items-center justify-center gap-8">
        <div className="flex flex-col items-center">
          <CircularProgress className="size-24" value={progress} />
          <span className="mt-2 text-muted-foreground text-sm">With value</span>
        </div>
 
        <div className="flex flex-col items-center">
          <CircularProgress className="size-24" hideText value={progress} />
          <span className="mt-2 text-muted-foreground text-sm">
            Indicator only
          </span>
        </div>
      </div>
 
      <div className="space-y-4">
        <Slider
          max={100}
          min={0}
          onValueChange={(value) => {
            setProgress(value[0] ?? 0);
          }}
          step={1}
          value={[progress]}
        />
      </div>
    </div>
  );
}

Without Text

You can hide the percentage text by using the hideText prop:

<CircularProgress hideText value={75} />

Custom Stroke Width

You can customize the thickness of the progress ring:

<CircularProgress strokeWidth={8} value={75} />

Dynamic Progress

You can update the value programmatically to show progress:

const [progress, setProgress] = useState(0);
 
useEffect(() => {
  const timer = setInterval(() => {
    setProgress((previousProgress) => {
      const nextProgress = previousProgress + 5;
      if (nextProgress >= 100) {
        clearInterval(timer);
        return 100;
      }
      return nextProgress;
    });
  }, 500);
 
  return () => {
    clearInterval(timer);
  };
}, []);
 
return <CircularProgress value={progress} />;

Props

PropTypeDefaultDescription
valuenumber-The progress value (0-100)
strokeWidthnumber4The width of the progress circle stroke
hideTextbooleanfalseWhether to hide the percentage text
classNamestring-Additional CSS class name