Most React spinners reset animation when component mounts. This leads to desynchronization and a jarring experience when the spinner is used in a sequence of components.
Persistent spinner synchronizes animation across all its instances.
persistent-spinner.mp4
Based on shadcn/ui spinner
import * as React from "react";
import { Loader2 } from "lucide-react";
interface SpinnerProps extends React.SVGProps<SVGSVGElement> {
className?: string;
}
function Spinner({ className, ...props }: SpinnerProps) {
const elementRef = React.useRef<SVGSVGElement>(null);
const duration = 1000;
React.useEffect(() => {
let animationFrameId: number;
const animate = (timestamp: number) => {
const rotationDegrees = ((timestamp / duration) % 1) * 360;
if (elementRef.current) {
elementRef.current.style.transform = `rotate(${rotationDegrees}deg)`;
}
animationFrameId = requestAnimationFrame(animate);
};
animationFrameId = requestAnimationFrame(animate);
return () => {
cancelAnimationFrame(animationFrameId);
};
}, [duration]);
return <Loader2 ref={elementRef} className={className} {...props} />;
}
export { Spinner };