Installation
npm i framer-motion lucide-react
import React, { useState, useEffect, useMemo, useCallback } from "react";
import { motion, AnimatePresence } from "framer-motion";

const AnimatedDigit = ({ value }) => (
    <div
        style={{
            position: "relative",
            width: "0.6em",
            height: "1.2em",
            display: "inline-block",
            overflow: "hidden",
        }}
    >
        <AnimatePresence mode="wait">
            <motion.span
                key={value}
                initial={{ y: "100%", filter: "blur(5px)" }}
                animate={{ y: 0, filter: "blur(0px)" }}
                exit={{ y: "-100%", filter: "blur(5px)" }}
                transition={{ duration: 0.3, ease: "easeInOut" }}
                style={{
                    position: "absolute",
                    left: 0,
                    right: 0,
                    top: 0,
                    bottom: 0,
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "center",
                }}
            >
                {value}
            </motion.span>
        </AnimatePresence>
    </div>
);

const useCountdown = (targetDate) => {
    const [timeLeft, setTimeLeft] = useState({});

    useEffect(() => {
        const calculateTimeLeft = () => {
            const difference = +new Date(targetDate) - +new Date();
            if (difference > 0) {
                return {
                    days: Math.floor(difference / (1000 * 60 * 60 * 24)),
                    hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
                    minutes: Math.floor((difference / 1000 / 60) % 60),
                    seconds: Math.floor((difference / 1000) % 60),
                };
            }
            return {};
        };

        setTimeLeft(calculateTimeLeft());
        const timer = setInterval(() => {
            setTimeLeft(calculateTimeLeft());
        }, 1000);

        return () => clearInterval(timer);
    }, [targetDate]);

    return timeLeft;
};

const TimeUnit = React.memo(({ interval, value }) => (
    <div className="flex flex-col items-center bg-secondary p-4 rounded-lg">
        <div className="text-4xl font-bold text-foreground px-2">
            {String(value)
                .padStart(2, "0")
                .split("")
                .map((digit, index) => (
                    <AnimatedDigit key={`${interval}-${index}`} value={digit} />
                ))}
        </div>
        <span className="text-sm text-foreground">{interval}</span>
    </div>
));

const Countdown = ({ targetDate }) => {
    const timeLeft = useCountdown(targetDate);
    const timeUnits = useMemo(() => Object.entries(timeLeft), [timeLeft]);

    return (
        <div className="flex justify-center items-center space-x-4">
            {timeUnits.map(([interval, value]) => (
                <TimeUnit key={interval} interval={interval} value={value} />
            ))}
        </div>
    );
};

export default Countdown;