A beautiful animated loader component with rotating gradient effects and text animations built with Tailwind CSS and Motion.dev .
npm i clsx tailwind-merge motionlib/utils.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}ShinyLoader.tsx
"use client"
import { motion } from "motion/react"
interface LoaderProps {
text?: string
size?: "sm" | "md" | "lg"
className?: string
}
const Loader = ({ text = "Generating", size = "md", className = "" }: LoaderProps) => {
const letters = text.split("")
const sizeConfig = {
sm: { container: "w-[120px] h-[120px]", text: "text-sm" },
md: { container: "w-[180px] h-[180px]", text: "text-xl" },
lg: { container: "w-[240px] h-[240px]", text: "text-2xl" },
}
const letterVariants = {
animate: {
opacity: [0.4, 1, 0.7, 0.4],
scale: [1, 1.15, 1, 1],
transition: {
duration: 2,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut" as const,
},
},
}
const loaderVariants = {
animate: {
rotate: [90, 270, 450],
transition: {
duration: 2,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut" as const,
},
},
}
const currentSize = sizeConfig[size]
return (
<div
className={`relative flex items-center justify-center ${currentSize.container} font-sans ${currentSize.text} font-light text-white rounded-full bg-transparent select-none ${className}`}
>
<div className="relative z-10 flex">
{letters.map((letter, index) => (
<motion.span
key={index}
className="inline-block opacity-40 rounded-full border-none"
variants={letterVariants}
animate="animate"
transition={{
delay: index * 0.1,
duration: 2,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut",
}}
>
{letter === " " ? "\u00A0" : letter}
</motion.span>
))}
</div>
<motion.div
className="absolute top-0 left-0 w-full aspect-square rounded-full bg-transparent z-0"
variants={loaderVariants}
animate="animate"
style={{
boxShadow: `
0 ${size === "sm" ? "6px" : size === "lg" ? "15px" : "10px"} ${size === "sm" ? "12px" : size === "lg" ? "25px" : "20px"} 0 #fff inset,
0 ${size === "sm" ? "12px" : size === "lg" ? "25px" : "20px"} ${size === "sm" ? "18px" : size === "lg" ? "35px" : "30px"} 0 #ad5fff inset,
0 ${size === "sm" ? "35px" : size === "lg" ? "75px" : "60px"} ${size === "sm" ? "35px" : size === "lg" ? "75px" : "60px"} 0 #471eec inset
`,
}}
/>
</div>
)
}
export default Loader| Prop | Type | Default | Description |
|---|---|---|---|
text | string | "Generating" | The text to display in the loader |
size | "sm" | "md" | "lg" | "md" | Size variant of the loader |
className | string | "" | Additional CSS classes |
| Size | Container | Text Size |
|---|---|---|
sm | 120x120px | text-sm |
md | 180x180px | text-xl |
lg | 240x240px | text-2xl |