Progress
Displays an indicator showing the completion progress of a task, typically displayed as a linear bar or circle.
Preview
"use client";
import { CircularProgress, LinearProgress } from "@/components/Progress";
import React from "react";
const Page = () => {
const [progress, setProgress] = React.useState(13);
React.useEffect(() => {
const timer = setTimeout(() => setProgress(66), 500);
return () => clearTimeout(timer);
}, []);
return (
<div className="flex flex-col gap-10 mt-6">
<LinearProgress value={progress} />
<div className="flex items-start">
<CircularProgress value={progress} size="xs" indicatorcolor="black" />
<CircularProgress value={progress} size="small" />
<CircularProgress value={progress} size="medium" />
<CircularProgress value={progress} size="large" />
<CircularProgress value={progress} size="xl" />
</div>
</div>
);
};
export default Page;
Examples
Linear progress with different sizes and colors
Circular progress with different sizes
Install the following dependencies:
npm install @radix-ui/react-progress
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import * as ProgressPrimitive from "@radix-ui/react-progress";
import { cn } from "@/lib/utils";
import { VariantProps, cva } from "class-variance-authority";
interface progressProps extends VariantProps<typeof circularVariants> {
indicatorcolor?: string;
}
const circularVariants = cva("", {
variants: {
size: {
xs: "w-16 h-16",
small: "w-40 h-40",
medium: "w-52 h-52",
large: "w-60 h-60",
xl: "w-64 h-64",
},
},
});
const LinearProgress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> & progressProps
>(({ indicatorcolor, className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
`relative w-36 h-3 overflow-hidden rounded-full bg-gray-300`,
className
)}
{...props}
>
<ProgressPrimitive.Indicator
className={`h-full w-full flex-1 ${indicatorcolor} bg-black transition-all`}
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
));
LinearProgress.displayName = ProgressPrimitive.Root.displayName;
const CircularProgress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> & progressProps
>(({ indicatorcolor, size, className, value, ...props }, ref) => {
const radius =
`${circularVariants({ size })}` === "w-16 h-16"
? 20
: `${circularVariants({ size })}` === "w-40 h-40"
? 35
: `${circularVariants({ size })}` === "w-52 h-52"
? 50
: `${circularVariants({ size })}` === "w-60 h-60"
? 70
: `${circularVariants({ size })}` === "w-64 h-64"
? 90
: 500;
const circumference = 2 * Math.PI * radius;
const dashoffset = circumference * (1 - (value || 0) / 100);
return (
<ProgressPrimitive.Root
ref={ref}
className={cn(
`relative ${circularVariants({ size })} overflow-hidden`,
className
)}
{...props}
>
<ProgressPrimitive.Indicator>
<svg className={`${circularVariants({ size })}`}>
<style>
{`
.circle-background {
fill: none;
stroke: #e6e6e6;
stroke-width: 10;
}
.circle-progress {
fill: none;
stroke: ${indicatorcolor};
stroke-width: 10;
transition:strokeDashoffset 0.3s ease;
}
`}
</style>
<circle className="circle-background" cx="50%" cy="50%" r={radius} />
<circle
className="circle-progress"
cx="50%"
cy="50%"
r={radius}
strokeDasharray={circumference}
strokeDashoffset={dashoffset}
/>
</svg>
</ProgressPrimitive.Indicator>
</ProgressPrimitive.Root>
);
});
CircularProgress.displayName = ProgressPrimitive.Root.displayName;
export { LinearProgress, CircularProgress };