Sparkles Text

Animated sparkle effects around text with configurable colors and density

animationtextsparkle
Install dependencies
$npm install framer-motion
Preview

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>
  );
}