Sparkles Text
Animated sparkle effects around text with configurable colors and density
animationtextsparkle
Install dependencies
$npm install framer-motionPreview
Source Code
"use client";
import { useEffect, useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
interface Sparkle {
id: string;
x: string;
y: string;
color: string;
delay: number;
size: number;
}
interface SparklesTextProps {
children: React.ReactNode;
colors?: string[];
className?: string;
}
function generateSparkle(colors: string[]): Sparkle {
return {
id: Math.random().toString(36).slice(2),
x: `${Math.random() * 100}%`,
y: `${Math.random() * 100}%`,
color: colors[Math.floor(Math.random() * colors.length)],
delay: Math.random() * 0.5,
size: Math.random() * 6 + 4,
};
}
export default function SparklesText({
children,
colors = ["#6366f1", "#8b5cf6", "#a78bfa"],
className = "",
}: SparklesTextProps) {
const [sparkles, setSparkles] = useState<Sparkle[]>([]);
useEffect(() => {
const interval = setInterval(() => {
setSparkles((prev) => {
const next = [...prev, generateSparkle(colors)];
if (next.length > 8) next.shift();
return next;
});
}, 300);
return () => clearInterval(interval);
}, [colors]);
return (
<span className={`relative inline-block ${className}`}>
<AnimatePresence>
{sparkles.map((sparkle) => (
<motion.svg
key={sparkle.id}
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0 }}
transition={{ duration: 0.5, delay: sparkle.delay }}
className="pointer-events-none absolute"
style={{ left: sparkle.x, top: sparkle.y }}
width={sparkle.size}
height={sparkle.size}
viewBox="0 0 24 24"
fill={sparkle.color}
>
<path d="M12 0L14.59 9.41L24 12L14.59 14.59L12 24L9.41 14.59L0 12L9.41 9.41L12 0Z" />
</motion.svg>
))}
</AnimatePresence>
<strong className="relative z-10 bg-gradient-to-r from-indigo-500 to-purple-500 bg-clip-text font-bold text-transparent">
{children}
</strong>
</span>
);
}