Loading Bar
A loading bar indicator
Made by lucas0%
Loading...import LoadingBar from "@/components/targetblank/components/loading-bar";
import { useTheme } from "next-themes";
import * as React from "react";
export const LoadingBarDemo = () => {
const { theme } = useTheme();
const [progress, setProgress] = React.useState(0);
const [isComplete, setIsComplete] = React.useState(false);
const [elapsedTime, setElapsedTime] = React.useState(0);
React.useEffect(() => {
if (isComplete) {
const timer = setTimeout(() => {
setProgress(0);
setElapsedTime(0);
setIsComplete(false);
}, 2000);
return () => clearTimeout(timer);
}
const interval = setInterval(() => {
setElapsedTime((t) => t + 1);
setProgress((prev) => {
if (prev >= 100) {
setIsComplete(true);
return 100;
}
const randomStep = Math.floor(Math.random() * 25) + 5;
return Math.min(prev + randomStep, 100);
});
}, 1000);
return () => clearInterval(interval);
}, [isComplete]);
const averageSpeed = elapsedTime > 0 ? progress / elapsedTime : 0;
const timeLeft =
averageSpeed > 0 ? Math.ceil((100 - progress) / averageSpeed) : 0;
const indicator =
progress < 100 && timeLeft > 0
? `About ${timeLeft} second${timeLeft > 1 ? "s" : ""} remaining...`
: elapsedTime > 0
? "Finished"
: "Loading...";
return (
<LoadingBar
color={theme === "dark" ? "#FFF" : "#000"}
progress={progress}
indicator={indicator}
completed={isComplete}
/>
);
};
Installation
Install the following dependencies:
Copy and paste the following code into your project:
import { cn } from "@/lib/utils";
import { CheckCircleIcon } from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
interface LoadingBarProps {
color?: string;
completed?: boolean;
finishedComponentClassName?: string;
finishedComponent?: React.ReactNode;
indicator?: string;
loadingClassName?: string;
progress: number;
}
const LoadingBar = ({
color = "#000",
completed = false,
finishedComponentClassName,
finishedComponent,
indicator,
loadingClassName,
progress,
}: LoadingBarProps) => {
return (
<div className="relative w-full h-full">
<AnimatePresence mode="wait">
{!completed ? (
<>
<div
key="bar"
className="relative w-full h-2 bg-gray-200 rounded-full dark:bg-gray-800"
>
<motion.div
initial={{ width: 0 }}
animate={{ width: `${progress}%` }}
className="h-full rounded-full transition-all duration-300"
style={{ background: color }}
/>
<span
className={cn(
"absolute top-0 right-0 z-10 text-xs font-medium text-black -translate-y-full",
loadingClassName,
)}
>
{progress}%
</span>
</div>
{indicator && (
<span className="mt-1 text-xs text-gray-400">{indicator}</span>
)}
</>
) : (
<motion.div
key="icon"
initial={{ opacity: 0, scale: 0.8, y: 10, rotate: 0 }}
animate={{ opacity: 1, scale: 1, y: 0, rotate: 360 }}
exit={{ opacity: 0, scale: 0.8, y: -10, rotate: 0 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
duration: 1,
}}
className={cn(
"flex justify-center items-center",
finishedComponentClassName,
)}
>
{finishedComponent || <CheckCircleIcon className="size-6" />}
</motion.div>
)}
</AnimatePresence>
</div>
);
};
export default LoadingBar;
Update the import paths to match your project setup.
Usage
<LoadingBar progress={75} />
Props
Prop | Type | Default |
---|---|---|
color? | string | #000 |
completed? | boolean | - |
finishedComponentClassName? | string | - |
finishedComponent? | React.ReactNode | - |
indicator? | string | - |
loadingClassName? | string | - |
progress | number | - |