React Bangla Logo
Reactবাংলা

Framer Motion দিয়ে Daily-Use Animation

প্রতিদিনের React app-এ যে animations লাগে — modal, toast, form, tab, card, scroll — সব একজায়গায়। Copy করুন, paste করুন, কাজ করুন।

Framer Motion: Daily-Use Animation Guide 🎨

আমি প্রতিদিন React app বানাই। আর প্রতিদিনই animation দরকার হয় —
button press করলে feedback চাই, modal খুললে smooth হোক, form submit হলে ইউজার বুঝুক কী হলো।

এই পুরো doc-টা আমার নিজের copy-paste library
আপনার জন্যও তাই। কোড দেখুন, বুঝুন, নিজের app-এ ব্যবহার করুন।


ইনস্টল করুন

npm install framer-motion

৩০ সেকেন্ডে Core Concepts

Framer Motion-এর পুরো power মাত্র কয়েকটা concept-এর উপর দাঁড়িয়ে —

Conceptকাজ
initialanimation শুরুর অবস্থা
animateanimation শেষের অবস্থা
exitcomponent সরে যাওয়ার সময়ের animation
transitionকতক্ষণ, কীভাবে animate হবে
whileHoverhover করলে যা হবে
whileTapclick/tap করলে যা হবে
whileInViewviewport-এ এলে animate হবে
variantsvariants-এর ভেতরে প্রতিটি state (hidden, visible, hover)
AnimatePresenceunmount-এর সময় exit animation
useScrollscroll position track করে
useTransformএকটা value থেকে আরেকটা তৈরি করে

এই ১১টা জিনিস ভালো বুঝলে Framer Motion দিয়ে যা চাই তাই বানানো যাবে।


১. Page Load Animation (Fade + Slide)

কোথায় লাগে: Hero section, page title, welcome screen, dashboard cards।

যেকোনো element mount হওয়ার সময় smooth করে আনতে initialanimate ব্যবহার করুন।

import { motion } from "framer-motion";

// Hero heading — নিচ থেকে উপরে আসবে
<motion.h1
  initial={{ opacity: 0, y: 24 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
>
  স্বাগতম!
</motion.h1>

// Subtitle — একটু দেরিতে আসবে
<motion.p
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  transition={{ delay: 0.2, duration: 0.5 }}
>
  Subtext...
</motion.p>

// CTA button — আরও দেরিতে
<motion.button
  initial={{ opacity: 0, y: 10 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ delay: 0.4, duration: 0.4 }}
>
  শুরু করুন →
</motion.button>

Pro tip: ease: [0.22, 1, 0.36, 1] হলো iOS-এর native spring easing — এটা সবচেয়ে natural লাগে।

import BasicAnimation from "./BasicAnimation";

export default function App() {
  return <BasicAnimation />;
}


২. Scroll-এ Card Reveal (whileInView)

কোথায় লাগে: Landing page features, blog posts, product cards, testimonials।

whileInView হলো সবচেয়ে বেশি ব্যবহৃত animation। Scroll করলে elements ধীরে ধীরে দেখা যায়।

import { motion } from "framer-motion";

// Grid বা list-এর প্রতিটা card
<motion.div
  initial={{ opacity: 0, y: 32 }}
  whileInView={{ opacity: 1, y: 0 }}
  viewport={{ once: true }} // একবারই animate হবে
  transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
>
  {/* card content */}
</motion.div>;

// Delay দিয়ে sequential effect
{
  items.map((item, i) => (
    <motion.div
      key={i}
      initial={{ opacity: 0, y: 24 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ delay: i * 0.08 }} // প্রতিটা একটু দেরিতে
    >
      {item}
    </motion.div>
  ));
}

viewport={{ once: true }} — শুধু প্রথমবার animate হবে, বারবার না।

import { motion } from "framer-motion";

const FEATURES = [
  {
    icon: "⚡",
    title: "GPU Accelerated",
    desc: "60fps animation, zero jank।",
    color: "#fef3c7",
  },
  {
    icon: "🎨",
    title: "সহজ API",
    desc: "initial, animate, transition — তিনটা prop-ই যথেষ্ট।",
    color: "#ede9fe",
  },
  {
    icon: "📱",
    title: "Gesture Support",
    desc: "hover, tap, drag — সব built-in।",
    color: "#dbeafe",
  },
  {
    icon: "🔁",
    title: "AnimatePresence",
    desc: "Mount/unmount-এ smooth exit animation।",
    color: "#dcfce7",
  },
  {
    icon: "📜",
    title: "Scroll-linked",
    desc: "useScroll দিয়ে parallax, progress bar।",
    color: "#fce7f3",
  },
  {
    icon: "🧩",
    title: "Variants",
    desc: "Parent-child stagger orchestration।",
    color: "#f0fdf4",
  },
];

export default function ScrollReveal() {
  return (
    <div style={{ padding: "16px 18px", fontFamily: "system-ui, sans-serif" }}>
      <motion.p
        initial={{ opacity: 0, y: -12 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true }}
        style={{
          textAlign: "center",
          fontWeight: 700,
          fontSize: 15,
          color: "#111",
          marginBottom: 16,
        }}
      >
        কেন Framer Motion? 🤔
      </motion.p>
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "1fr 1fr",
          gap: 10,
        }}
      >
        {FEATURES.map((f, i) => (
          <motion.div
            key={i}
            initial={{ opacity: 0, y: 28 }}
            whileInView={{ opacity: 1, y: 0 }}
            viewport={{ once: true }}
            transition={{
              delay: i * 0.07,
              duration: 0.45,
              ease: [0.22, 1, 0.36, 1],
            }}
            whileHover={{ y: -4, boxShadow: "0 12px 32px rgba(0,0,0,0.08)" }}
            style={{
              padding: 14,
              background: f.color,
              borderRadius: 14,
              cursor: "default",
            }}
          >
            <div style={{ fontSize: 22, marginBottom: 6 }}>{f.icon}</div>
            <p
              style={{
                fontWeight: 700,
                fontSize: 12,
                color: "#111",
                margin: "0 0 4px",
              }}
            >
              {f.title}
            </p>
            <p
              style={{
                fontSize: 11,
                color: "#555",
                margin: 0,
                lineHeight: 1.5,
              }}
            >
              {f.desc}
            </p>
          </motion.div>
        ))}
      </div>
    </div>
  );
}


৩. Button Interactions (whileHover + whileTap)

কোথায় লাগে: প্রতিটা button, icon button, like/save action, CTA।

Button-এ animation না থাকলে app dead মনে হয়। whileHover + whileTap দিয়ে প্রাণ দিন।

import { motion } from "framer-motion";

// Primary button
<motion.button
  whileHover={{ scale: 1.05, boxShadow: "0 12px 32px rgba(0,0,0,0.15)" }}
  whileTap={{ scale: 0.95 }}
  transition={{ type: "spring", stiffness: 400, damping: 25 }}
>
  Submit
</motion.button>

// Icon button — rotate on hover
<motion.button
  whileHover={{ scale: 1.15, rotate: 15 }}
  whileTap={{ scale: 0.85 }}
>
  ⚙️
</motion.button>

// Like button — bounce on click
<motion.button
  onClick={() => setLiked(!liked)}
  animate={liked ? { scale: [1, 1.3, 1] } : {}}
  transition={{ duration: 0.3 }}
>
  {liked ? "❤️" : "🤍"}
</motion.button>
import { motion } from "framer-motion";
import { useState } from "react";

export default function GestureAnimation() {
  const [liked, setLiked] = useState(false);
  const [saved, setSaved] = useState(false);

  return (
    <div
      style={{
        padding: 28,
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        gap: 16,
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <p
        style={{ color: "#9ca3af", fontSize: 12, margin: 0, letterSpacing: 1 }}
      >
        HOVER & TAP
      </p>

      {/* Primary CTA */}
      <motion.button
        whileHover={{
          scale: 1.05,
          boxShadow: "0 16px 40px rgba(102,126,234,0.45)",
        }}
        whileTap={{ scale: 0.95 }}
        transition={{ type: "spring", stiffness: 400, damping: 25 }}
        style={{
          padding: "13px 32px",
          background: "linear-gradient(135deg, #667eea, #764ba2)",
          color: "white",
          border: "none",
          borderRadius: 12,
          fontWeight: 700,
          fontSize: 15,
          cursor: "pointer",
          boxShadow: "0 8px 24px rgba(102,126,234,0.35)",
        }}
      >
        Get Started →
      </motion.button>

      {/* Like + Save row */}
      <div style={{ display: "flex", gap: 10 }}>
        <motion.button
          onClick={() => setLiked(!liked)}
          whileTap={{ scale: 0.85 }}
          animate={liked ? { scale: [1, 1.3, 1] } : {}}
          transition={{ duration: 0.3 }}
          style={{
            display: "flex",
            alignItems: "center",
            gap: 6,
            padding: "9px 18px",
            background: liked ? "#fee2e2" : "#f3f4f6",
            color: liked ? "#ef4444" : "#6b7280",
            border: "none",
            borderRadius: 999,
            fontWeight: 600,
            fontSize: 13,
            cursor: "pointer",
          }}
        >
          <motion.span
            animate={{ rotate: liked ? [0, -20, 20, 0] : 0 }}
            transition={{ duration: 0.4 }}
          >
            {liked ? "❤️" : "🤍"}
          </motion.span>
          {liked ? "Liked" : "Like"}
        </motion.button>

        <motion.button
          onClick={() => setSaved(!saved)}
          whileTap={{ scale: 0.85 }}
          style={{
            display: "flex",
            alignItems: "center",
            gap: 6,
            padding: "9px 18px",
            background: saved ? "#ede9fe" : "#f3f4f6",
            color: saved ? "#7c3aed" : "#6b7280",
            border: "none",
            borderRadius: 999,
            fontWeight: 600,
            fontSize: 13,
            cursor: "pointer",
          }}
        >
          {saved ? "🔖" : "📄"} {saved ? "Saved" : "Save"}
        </motion.button>
      </div>

      {/* Icon button */}
      <motion.button
        whileHover={{ scale: 1.15, rotate: 15 }}
        whileTap={{ scale: 0.85 }}
        transition={{ type: "spring", stiffness: 500 }}
        style={{
          width: 44,
          height: 44,
          borderRadius: "50%",
          background: "#f3f4f6",
          border: "none",
          fontSize: 20,
          cursor: "pointer",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        ⚙️
      </motion.button>
    </div>
  );
}


৪. Stagger List (একের পর এক আসবে)

কোথায় লাগে: Notification list, search results, menu items, feed।

variants + staggerChildren দিয়ে parent-child orchestration — প্রতিটা child একটু দেরিতে animate হয়।

import { motion } from "framer-motion";

const container = {
  hidden: {},
  show: {
    transition: { staggerChildren: 0.1 }, // প্রতিটা item 100ms দেরিতে
  },
};

const item = {
  hidden: { opacity: 0, x: -20 },
  show: {
    opacity: 1,
    x: 0,
    transition: { type: "spring", stiffness: 300, damping: 24 },
  },
};

function NotificationList({ notifications }) {
  return (
    <motion.div variants={container} initial="hidden" animate="show">
      {notifications.map((n, i) => (
        <motion.div key={i} variants={item}>
          {n.title}
        </motion.div>
      ))}
    </motion.div>
  );
}
import { motion } from "framer-motion";

const NOTIFICATIONS = [
  {
    icon: "💬",
    title: "নতুন মেসেজ",
    msg: "Rafiq তোমাকে message করেছে",
    time: "এইমাত্র",
    bg: "#ede9fe",
  },
  {
    icon: "🔔",
    title: "নতুন অর্ডার",
    msg: "Order #1042 placed successfully",
    time: "২ মিনিট",
    bg: "#dbeafe",
  },
  {
    icon: "✅",
    title: "Deploy সফল",
    msg: "Production deploy সম্পন্ন হয়েছে",
    time: "৫ মিনিট",
    bg: "#dcfce7",
  },
  {
    icon: "⚠️",
    title: "সতর্কবার্তা",
    msg: "Server CPU 85% এ পৌঁছেছে",
    time: "১০ মিনিট",
    bg: "#fef3c7",
  },
];

const container = {
  hidden: {},
  show: { transition: { staggerChildren: 0.1 } },
};

const item = {
  hidden: { opacity: 0, x: -20, scale: 0.97 },
  show: {
    opacity: 1,
    x: 0,
    scale: 1,
    transition: { type: "spring", stiffness: 320, damping: 26 },
  },
};

export default function StaggerList() {
  return (
    <div
      style={{
        padding: 20,
        maxWidth: 360,
        margin: "0 auto",
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <p
        style={{
          fontWeight: 700,
          fontSize: 14,
          color: "#111",
          margin: "0 0 14px",
        }}
      >
        🔔 Notifications
      </p>
      <motion.div
        variants={container}
        initial="hidden"
        animate="show"
        style={{ display: "flex", flexDirection: "column", gap: 9 }}
      >
        {NOTIFICATIONS.map((n, i) => (
          <motion.div
            key={i}
            variants={item}
            whileHover={{ x: 4 }}
            style={{
              display: "flex",
              alignItems: "center",
              gap: 12,
              padding: "11px 13px",
              borderRadius: 13,
              background: n.bg,
              cursor: "pointer",
            }}
          >
            <span style={{ fontSize: 20 }}>{n.icon}</span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <p
                style={{
                  fontWeight: 600,
                  fontSize: 13,
                  margin: 0,
                  color: "#111",
                }}
              >
                {n.title}
              </p>
              <p
                style={{
                  fontSize: 11,
                  margin: "2px 0 0",
                  color: "#555",
                  whiteSpace: "nowrap",
                  overflow: "hidden",
                  textOverflow: "ellipsis",
                }}
              >
                {n.msg}
              </p>
            </div>
            <span
              style={{
                fontSize: 10,
                color: "#9ca3af",
                flexShrink: 0,
                fontWeight: 500,
              }}
            >
              {n.time}
            </span>
          </motion.div>
        ))}
      </motion.div>
    </div>
  );
}


৫. Tab Switcher (AnimatePresence)

কোথায় লাগে: Dashboard tabs, settings pages, profile sections, bottom navigation।

AnimatePresence দিয়ে active tab বদলানোর সময় content smooth করে transition করে।
layoutId দিয়ে indicator pill animate হয়।

import { motion, AnimatePresence } from "framer-motion";

// Tab indicator — layoutId দিয়ে smooth slide হয়
{
  active === tab.id && (
    <motion.div
      layoutId="tab-indicator"
      style={{ position: "absolute", inset: 0, background: "white" }}
      transition={{ type: "spring", stiffness: 400, damping: 30 }}
    />
  );
}

// Tab content — AnimatePresence দিয়ে exit animation
<AnimatePresence mode="wait">
  <motion.div
    key={activeTab} // key বদলালে exit + enter animate হয়
    initial={{ opacity: 0, y: 8 }}
    animate={{ opacity: 1, y: 0 }}
    exit={{ opacity: 0, y: -8 }}
    transition={{ duration: 0.2 }}
  >
    {tabContent}
  </motion.div>
</AnimatePresence>;
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";

const TABS = [
  {
    id: "home",
    icon: "🏠",
    label: "Home",
    content: {
      heading: "ড্যাশবোর্ড",
      body: "আজকের summary, সাম্প্রতিক activities এবং quick actions এখানে দেখানো হয়। এই section-এ hero content, stats এবং overview cards রাখুন।",
      color: "#ede9fe",
    },
  },
  {
    id: "explore",
    icon: "🔍",
    label: "Explore",
    content: {
      heading: "অন্বেষণ করুন",
      body: "নতুন content, trending topics এবং recommended items এখানে থাকে। Discovery-based UI-তে এই pattern সবচেয়ে বেশি ব্যবহার হয়।",
      color: "#dbeafe",
    },
  },
  {
    id: "profile",
    icon: "👤",
    label: "Profile",
    content: {
      heading: "আপনার প্রোফাইল",
      body: "User তথ্য, avatar, bio এবং settings এখানে। Account management এবং personalization options রাখুন এই section-এ।",
      color: "#dcfce7",
    },
  },
];

export default function TabAnimation() {
  const [active, setActive] = useState("home");

  return (
    <div
      style={{
        padding: 24,
        maxWidth: 380,
        margin: "0 auto",
        fontFamily: "system-ui, sans-serif",
      }}
    >
      {/* Pill tab bar */}
      <div
        style={{
          display: "flex",
          background: "#f3f4f6",
          borderRadius: 14,
          padding: 4,
          gap: 3,
          marginBottom: 16,
        }}
      >
        {TABS.map((tab) => (
          <button
            key={tab.id}
            onClick={() => setActive(tab.id)}
            style={{
              flex: 1,
              padding: "9px 6px",
              border: "none",
              background: "transparent",
              borderRadius: 11,
              fontSize: 12,
              fontWeight: 600,
              cursor: "pointer",
              position: "relative",
              color: active === tab.id ? "#111" : "#9ca3af",
              transition: "color 0.15s",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              gap: 4,
            }}
          >
            {active === tab.id && (
              <motion.div
                layoutId="tab-pill"
                style={{
                  position: "absolute",
                  inset: 0,
                  background: "white",
                  borderRadius: 11,
                  boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
                }}
                transition={{ type: "spring", stiffness: 400, damping: 30 }}
              />
            )}
            <span style={{ position: "relative", zIndex: 1 }}>{tab.icon}</span>
            <span style={{ position: "relative", zIndex: 1 }}>{tab.label}</span>
          </button>
        ))}
      </div>

      {/* Tab content with AnimatePresence */}
      <AnimatePresence mode="wait">
        {TABS.map(
          (tab) =>
            tab.id === active && (
              <motion.div
                key={tab.id}
                initial={{ opacity: 0, y: 10 }}
                animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: -10 }}
                transition={{ duration: 0.2, ease: "easeOut" }}
                style={{
                  padding: 20,
                  background: tab.content.color,
                  borderRadius: 16,
                  minHeight: 120,
                }}
              >
                <p
                  style={{
                    fontWeight: 700,
                    fontSize: 15,
                    color: "#111",
                    margin: "0 0 8px",
                  }}
                >
                  {tab.icon} {tab.content.heading}
                </p>
                <p
                  style={{
                    fontSize: 13,
                    color: "#374151",
                    margin: 0,
                    lineHeight: 1.65,
                  }}
                >
                  {tab.content.body}
                </p>
              </motion.div>
            ),
        )}
      </AnimatePresence>

      {/* Bottom nav example */}
      <div
        style={{
          marginTop: 24,
          padding: "12px 8px",
          background: "white",
          borderRadius: 16,
          boxShadow: "0 -2px 16px rgba(0,0,0,0.06)",
          display: "flex",
          justifyContent: "space-around",
        }}
      >
        {TABS.map((tab) => (
          <motion.button
            key={tab.id}
            onClick={() => setActive(tab.id)}
            whileTap={{ scale: 0.85 }}
            style={{
              background: "none",
              border: "none",
              cursor: "pointer",
              display: "flex",
              flexDirection: "column",
              alignItems: "center",
              gap: 3,
            }}
          >
            <motion.span
              animate={{
                scale: active === tab.id ? 1.25 : 1,
                filter:
                  active === tab.id ? "none" : "grayscale(1) opacity(0.5)",
              }}
              style={{ fontSize: 22 }}
            >
              {tab.icon}
            </motion.span>
            {active === tab.id && (
              <motion.div
                layoutId="nav-dot"
                style={{
                  width: 4,
                  height: 4,
                  background: "#667eea",
                  borderRadius: "50%",
                }}
              />
            )}
          </motion.button>
        ))}
      </div>
    </div>
  );
}


৬. Modal / Confirmation Dialog

কোথায় লাগে: Delete confirm, logout confirm, payment confirmation, any important action।

Backdrop blur + modal scale-in। AnimatePresence দিয়ে close animation হয়।

import { motion, AnimatePresence } from "framer-motion";

<AnimatePresence>
  {isOpen && (
    <>
      {/* Backdrop */}
      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        onClick={() => setIsOpen(false)}
        style={{
          position: "fixed",
          inset: 0,
          background: "rgba(0,0,0,0.45)",
          backdropFilter: "blur(4px)",
          zIndex: 40,
        }}
      />

      {/* Modal */}
      <motion.div
        initial={{ opacity: 0, scale: 0.88, y: 24 }}
        animate={{ opacity: 1, scale: 1, y: 0 }}
        exit={{ opacity: 0, scale: 0.88, y: 24 }}
        transition={{ type: "spring", stiffness: 320, damping: 28 }}
        style={{
          position: "fixed",
          top: "50%",
          left: "50%",
          transform: "translate(-50%, -50%)",
          background: "white",
          borderRadius: 20,
          padding: 28,
          zIndex: 50,
        }}
      >
        {/* modal content */}
      </motion.div>
    </>
  )}
</AnimatePresence>;

দুটো জিনিস মাথায় রাখুন:

  1. Backdrop আর Modal-এ আলাদা key দিন
  2. AnimatePresence-এর বাইরে condition রাখুন
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";

export default function ModalAnimation() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div
      style={{
        padding: 32,
        display: "flex",
        justifyContent: "center",
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <motion.button
        onClick={() => setIsOpen(true)}
        whileHover={{ scale: 1.05 }}
        whileTap={{ scale: 0.95 }}
        style={{
          padding: "12px 28px",
          background: "linear-gradient(135deg, #667eea, #764ba2)",
          color: "white",
          border: "none",
          borderRadius: 10,
          fontWeight: 600,
          fontSize: 15,
          cursor: "pointer",
          boxShadow: "0 8px 24px rgba(102,126,234,0.35)",
        }}
      >
        Modal খুলুন ✨
      </motion.button>

      <AnimatePresence>
        {isOpen && (
          <>
            {/* Backdrop */}
            <motion.div
              key="backdrop"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              onClick={() => setIsOpen(false)}
              style={{
                position: "fixed",
                inset: 0,
                background: "rgba(0,0,0,0.45)",
                backdropFilter: "blur(4px)",
                zIndex: 40,
              }}
            />
            {/* Modal */}
            <motion.div
              key="modal"
              initial={{ opacity: 0, scale: 0.88, y: 24 }}
              animate={{ opacity: 1, scale: 1, y: 0 }}
              exit={{ opacity: 0, scale: 0.88, y: 24 }}
              transition={{ type: "spring", stiffness: 320, damping: 28 }}
              style={{
                position: "fixed",
                top: "50%",
                left: "50%",
                transform: "translate(-50%, -50%)",
                background: "white",
                borderRadius: 20,
                padding: 28,
                width: 320,
                zIndex: 50,
                boxShadow: "0 24px 64px rgba(0,0,0,0.2)",
              }}
            >
              <motion.div
                initial={{ scale: 0 }}
                animate={{ scale: 1 }}
                transition={{
                  delay: 0.15,
                  type: "spring",
                  stiffness: 400,
                  damping: 20,
                }}
                style={{
                  fontSize: 40,
                  textAlign: "center",
                  marginBottom: 12,
                }}
              >
                🗑️
              </motion.div>
              <h3
                style={{
                  margin: "0 0 8px",
                  fontSize: 17,
                  color: "#111",
                  textAlign: "center",
                }}
              >
                নিশ্চিত করুন
              </h3>
              <p
                style={{
                  color: "#6b7280",
                  fontSize: 13,
                  margin: "0 0 24px",
                  textAlign: "center",
                  lineHeight: 1.6,
                }}
              >
                আপনি কি সত্যিই এই ফাইলটি মুছে ফেলতে চান? এটা পূর্বাবস্থায়
                ফেরানো যাবে না।
              </p>
              <div style={{ display: "flex", gap: 10 }}>
                <motion.button
                  whileTap={{ scale: 0.95 }}
                  onClick={() => setIsOpen(false)}
                  style={{
                    flex: 1,
                    padding: "10px 0",
                    border: "1.5px solid #e5e7eb",
                    borderRadius: 10,
                    background: "white",
                    fontWeight: 600,
                    cursor: "pointer",
                    fontSize: 14,
                    color: "#374151",
                  }}
                >
                  বাতিল
                </motion.button>
                <motion.button
                  whileTap={{ scale: 0.95 }}
                  onClick={() => setIsOpen(false)}
                  style={{
                    flex: 1,
                    padding: "10px 0",
                    border: "none",
                    borderRadius: 10,
                    background: "linear-gradient(135deg, #ef4444, #dc2626)",
                    fontWeight: 600,
                    cursor: "pointer",
                    fontSize: 14,
                    color: "white",
                  }}
                >
                  মুছে ফেলুন
                </motion.button>
              </div>
            </motion.div>
          </>
        )}
      </AnimatePresence>
    </div>
  );
}


৭. Toast Notification

কোথায় লাগে: Success/error feedback, form submission result, real-time alerts।

AnimatePresence + layout prop দিয়ে toast stack animate হয়।
Right corner থেকে slide-in, timeout-এ slide-out।

import { motion, AnimatePresence } from "framer-motion";

// Toast stack container
<div style={{ position: "fixed", bottom: 20, right: 20 }}>
  <AnimatePresence initial={false}>
    {toasts.map((toast) => (
      <motion.div
        key={toast.id}
        layout // নতুন toast আসলে stack reflow animate হয়
        initial={{ opacity: 0, x: 60, scale: 0.92 }}
        animate={{ opacity: 1, x: 0, scale: 1 }}
        exit={{ opacity: 0, x: 60, scale: 0.88 }}
        transition={{ type: "spring", stiffness: 280, damping: 24 }}
      >
        {toast.message}
      </motion.div>
    ))}
  </AnimatePresence>
</div>;
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";

const TOAST_TYPES = {
  success: {
    bg: "#f0fdf4",
    border: "#bbf7d0",
    color: "#166534",
    icon: "✅",
    label: "সফল হয়েছে!",
  },
  error: {
    bg: "#fff1f2",
    border: "#fecdd3",
    color: "#9f1239",
    icon: "❌",
    label: "ত্রুটি ঘটেছে!",
  },
  info: {
    bg: "#eff6ff",
    border: "#bfdbfe",
    color: "#1e40af",
    icon: "ℹ️",
    label: "জানা দরকার",
  },
  warning: {
    bg: "#fffbeb",
    border: "#fde68a",
    color: "#92400e",
    icon: "⚠️",
    label: "সতর্ক থাকুন",
  },
};

export default function ToastAnimation() {
  const [toasts, setToasts] = useState([]);

  const addToast = (type) => {
    const id = Date.now();
    setToasts((prev) =>
      [{ id, type, ...TOAST_TYPES[type] }, ...prev].slice(0, 5),
    );
    setTimeout(() => {
      setToasts((prev) => prev.filter((t) => t.id !== id));
    }, 3500);
  };

  const remove = (id) => setToasts((prev) => prev.filter((t) => t.id !== id));

  return (
    <div
      style={{
        padding: 24,
        fontFamily: "system-ui, sans-serif",
        minHeight: 200,
      }}
    >
      <p
        style={{
          textAlign: "center",
          fontSize: 13,
          color: "#6b7280",
          marginBottom: 16,
        }}
      >
        Button চাপুন → toast দেখুন
      </p>

      {/* Trigger buttons */}
      <div
        style={{
          display: "flex",
          flexWrap: "wrap",
          gap: 8,
          justifyContent: "center",
        }}
      >
        {Object.keys(TOAST_TYPES).map((type) => (
          <motion.button
            key={type}
            onClick={() => addToast(type)}
            whileHover={{ scale: 1.05 }}
            whileTap={{ scale: 0.93 }}
            style={{
              padding: "8px 16px",
              borderRadius: 9,
              border: `1.5px solid ${TOAST_TYPES[type].border}`,
              background: TOAST_TYPES[type].bg,
              color: TOAST_TYPES[type].color,
              fontWeight: 600,
              fontSize: 12,
              cursor: "pointer",
            }}
          >
            {TOAST_TYPES[type].icon} {type}
          </motion.button>
        ))}
      </div>

      {/* Toast stack — bottom right */}
      <div
        style={{
          position: "fixed",
          bottom: 20,
          right: 20,
          display: "flex",
          flexDirection: "column",
          gap: 8,
          zIndex: 999,
          maxWidth: 300,
        }}
      >
        <AnimatePresence initial={false}>
          {toasts.map((toast) => (
            <motion.div
              key={toast.id}
              layout
              initial={{ opacity: 0, x: 60, scale: 0.92 }}
              animate={{ opacity: 1, x: 0, scale: 1 }}
              exit={{ opacity: 0, x: 60, scale: 0.88 }}
              transition={{ type: "spring", stiffness: 280, damping: 24 }}
              onClick={() => remove(toast.id)}
              style={{
                display: "flex",
                alignItems: "flex-start",
                gap: 10,
                padding: "12px 14px",
                background: toast.bg,
                border: `1.5px solid ${toast.border}`,
                borderRadius: 14,
                boxShadow: "0 8px 28px rgba(0,0,0,0.1)",
                cursor: "pointer",
                userSelect: "none",
              }}
            >
              <span style={{ fontSize: 18, lineHeight: 1 }}>{toast.icon}</span>
              <div style={{ flex: 1 }}>
                <p
                  style={{
                    fontWeight: 700,
                    fontSize: 13,
                    color: toast.color,
                    margin: 0,
                  }}
                >
                  {toast.label}
                </p>
                <p
                  style={{
                    fontSize: 11,
                    color: toast.color,
                    opacity: 0.75,
                    margin: "2px 0 0",
                  }}
                >
                  Click করে বন্ধ করুন
                </p>
              </div>
            </motion.div>
          ))}
        </AnimatePresence>
      </div>
    </div>
  );
}


৮. Form Animation (Validation + Success State)

কোথায় লাগে: Login form, signup form, contact form, subscribe form।

তিনটা animation একসাথে —

  • Input shake on validation error
  • Spinner while loading
  • Success state with celebration
import { motion, AnimatePresence } from "framer-motion";

// Input shake — validate error হলে
<motion.input
  animate={error ? { x: [-6, 6, -4, 4, 0] } : {}}
  transition={{ duration: 0.35 }}
  style={{ border: `1.5px solid ${error ? "#ef4444" : "#e5e7eb"}` }}
/>

// Error message — slide in/out
<AnimatePresence>
  {error && (
    <motion.p
      initial={{ opacity: 0, height: 0 }}
      animate={{ opacity: 1, height: "auto" }}
      exit={{ opacity: 0, height: 0 }}
    >
      {error}
    </motion.p>
  )}
</AnimatePresence>

// Step switch — form → loading → success
<AnimatePresence mode="wait">
  <motion.div
    key={step}                              // step বদলালেই নতুন animation
    initial={{ opacity: 0, y: 16 }}
    animate={{ opacity: 1, y: 0 }}
    exit={{ opacity: 0, y: -16 }}
  >
    {/* current step content */}
  </motion.div>
</AnimatePresence>
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";

export default function FormAnimation() {
  const [step, setStep] = useState("form"); // form | loading | success
  const [email, setEmail] = useState("");
  const [emailError, setEmailError] = useState("");

  const validate = () => {
    if (!email || !email.includes("@") || !email.includes(".")) {
      setEmailError("সঠিক email address দিন");
      return false;
    }
    setEmailError("");
    return true;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!validate()) return;
    setStep("loading");
    setTimeout(() => setStep("success"), 2200);
  };

  return (
    <div
      style={{
        padding: 24,
        display: "flex",
        justifyContent: "center",
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <div
        style={{
          width: 320,
          background: "white",
          borderRadius: 20,
          padding: 28,
          boxShadow: "0 8px 32px rgba(0,0,0,0.1)",
        }}
      >
        <AnimatePresence mode="wait">
          {/* ── FORM STATE ── */}
          {step === "form" && (
            <motion.form
              key="form"
              initial={{ opacity: 0, y: 16 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -16 }}
              transition={{ duration: 0.25 }}
              onSubmit={handleSubmit}
              style={{ display: "flex", flexDirection: "column", gap: 16 }}
            >
              <div>
                <h3
                  style={{
                    margin: 0,
                    fontSize: 18,
                    color: "#111",
                    fontWeight: 700,
                  }}
                >
                  ✉️ Newsletter
                </h3>
                <p
                  style={{
                    color: "#6b7280",
                    fontSize: 13,
                    margin: "6px 0 0",
                    lineHeight: 1.5,
                  }}
                >
                  সাপ্তাহিক React tips পান। Unsubscribe যেকোনো সময়।
                </p>
              </div>

              {/* Email field */}
              <div>
                <label
                  style={{
                    display: "block",
                    fontSize: 12,
                    fontWeight: 600,
                    color: "#374151",
                    marginBottom: 6,
                  }}
                >
                  Email Address
                </label>
                <motion.input
                  type="text"
                  value={email}
                  onChange={(e) => {
                    setEmail(e.target.value);
                    if (emailError) setEmailError("");
                  }}
                  placeholder="you@example.com"
                  animate={emailError ? { x: [-6, 6, -4, 4, 0] } : {}}
                  transition={{ duration: 0.35 }}
                  style={{
                    width: "100%",
                    padding: "10px 12px",
                    borderRadius: 10,
                    border: `1.5px solid ${emailError ? "#ef4444" : "#e5e7eb"}`,
                    fontSize: 14,
                    outline: "none",
                    boxSizing: "border-box",
                    transition: "border-color 0.2s",
                  }}
                />
                <AnimatePresence>
                  {emailError && (
                    <motion.p
                      initial={{ opacity: 0, height: 0, y: -4 }}
                      animate={{ opacity: 1, height: "auto", y: 0 }}
                      exit={{ opacity: 0, height: 0 }}
                      transition={{ duration: 0.2 }}
                      style={{
                        color: "#ef4444",
                        fontSize: 12,
                        margin: "5px 0 0",
                        fontWeight: 500,
                      }}
                    >
                      ⚠️ {emailError}
                    </motion.p>
                  )}
                </AnimatePresence>
              </div>

              <motion.button
                type="submit"
                whileHover={{ scale: 1.02 }}
                whileTap={{ scale: 0.97 }}
                style={{
                  padding: "12px",
                  background: "linear-gradient(135deg, #667eea, #764ba2)",
                  color: "white",
                  border: "none",
                  borderRadius: 10,
                  fontWeight: 600,
                  fontSize: 14,
                  cursor: "pointer",
                  boxShadow: "0 6px 20px rgba(102,126,234,0.35)",
                }}
              >
                Subscribe করুন →
              </motion.button>
            </motion.form>
          )}

          {/* ── LOADING STATE ── */}
          {step === "loading" && (
            <motion.div
              key="loading"
              initial={{ opacity: 0, scale: 0.9 }}
              animate={{ opacity: 1, scale: 1 }}
              exit={{ opacity: 0, scale: 0.9 }}
              transition={{ duration: 0.2 }}
              style={{ textAlign: "center", padding: "32px 0" }}
            >
              <motion.div
                animate={{ rotate: 360 }}
                transition={{ duration: 0.9, repeat: Infinity, ease: "linear" }}
                style={{
                  width: 44,
                  height: 44,
                  margin: "0 auto 16px",
                  borderRadius: "50%",
                  border: "3px solid #e5e7eb",
                  borderTopColor: "#667eea",
                }}
              />
              <p style={{ color: "#6b7280", fontSize: 14, margin: 0 }}>
                Subscribe হচ্ছে...
              </p>
            </motion.div>
          )}

          {/* ── SUCCESS STATE ── */}
          {step === "success" && (
            <motion.div
              key="success"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              style={{ textAlign: "center", padding: "12px 0" }}
            >
              <motion.div
                initial={{ scale: 0, rotate: -20 }}
                animate={{ scale: 1, rotate: 0 }}
                transition={{
                  delay: 0.1,
                  type: "spring",
                  stiffness: 380,
                  damping: 20,
                }}
                style={{ fontSize: 52, marginBottom: 14 }}
              >
                🎉
              </motion.div>
              <motion.h3
                initial={{ opacity: 0, y: 8 }}
                animate={{ opacity: 1, y: 0 }}
                transition={{ delay: 0.3 }}
                style={{ margin: "0 0 8px", color: "#111", fontSize: 18 }}
              >
                সফল হয়েছে!
              </motion.h3>
              <motion.p
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                transition={{ delay: 0.45 }}
                style={{
                  color: "#6b7280",
                  fontSize: 13,
                  margin: "0 0 20px",
                  lineHeight: 1.6,
                }}
              >
                <strong>{email}</strong> — subscribe করা হয়েছে।
              </motion.p>
              <motion.button
                whileTap={{ scale: 0.95 }}
                onClick={() => {
                  setStep("form");
                  setEmail("");
                }}
                style={{
                  padding: "9px 22px",
                  border: "1.5px solid #e5e7eb",
                  borderRadius: 9,
                  background: "white",
                  fontSize: 13,
                  fontWeight: 600,
                  cursor: "pointer",
                  color: "#374151",
                }}
              >
                আবার করুন
              </motion.button>
            </motion.div>
          )}
        </AnimatePresence>
      </div>
    </div>
  );
}


৯. Card Hover Effects

কোথায় লাগে: Project cards, product cards, blog cards, team member cards।

Lift effect + shadow। Simple কিন্তু premium feel দেয়।

import { motion } from "framer-motion";

<motion.div
  whileHover={{
    y: -6,
    boxShadow: "0 20px 48px rgba(0,0,0,0.12)",
  }}
  transition={{ type: "spring", stiffness: 300, damping: 20 }}
  style={{ borderRadius: 16, background: "white", cursor: "pointer" }}
>
  {/* card content */}
</motion.div>;

// Staggered card list — scroll করলে একের পর এক আসবে
const cardVariants = {
  hidden: { opacity: 0, y: 24 },
  visible: (i) => ({
    opacity: 1,
    y: 0,
    transition: { delay: i * 0.1, duration: 0.4 },
  }),
};

{
  cards.map((card, i) => (
    <motion.div
      key={i}
      custom={i}
      variants={cardVariants}
      initial="hidden"
      animate="visible"
      whileHover={{ y: -4 }}
    >
      {/* card */}
    </motion.div>
  ));
}
import { motion } from "framer-motion";

const PROJECTS = [
  {
    icon: "🛒",
    title: "E-Commerce Platform",
    desc: "React + Node.js দিয়ে বানানো full-stack online shop।",
    tags: ["React", "Node.js", "MongoDB"],
    gradient: "linear-gradient(135deg, #667eea, #764ba2)",
    stat: "12k users",
  },
  {
    icon: "🌤️",
    title: "Weather Dashboard",
    desc: "Real-time weather data এবং 7-day forecast সহ।",
    tags: ["React", "REST API", "Chart.js"],
    gradient: "linear-gradient(135deg, #4facfe, #00f2fe)",
    stat: "5 cities",
  },
  {
    icon: "✅",
    title: "Task Manager",
    desc: "Drag & drop Kanban board — team productivity tool।",
    tags: ["React", "DnD Kit", "Firebase"],
    gradient: "linear-gradient(135deg, #43e97b, #38f9d7)",
    stat: "3 boards",
  },
  {
    icon: "📊",
    title: "Analytics Dashboard",
    desc: "Sales, users এবং revenue-এর interactive charts।",
    tags: ["Next.js", "Recharts", "Prisma"],
    gradient: "linear-gradient(135deg, #f093fb, #f5576c)",
    stat: "$48k MRR",
  },
];

const cardVariants = {
  hidden: { opacity: 0, y: 24 },
  visible: (i) => ({
    opacity: 1,
    y: 0,
    transition: { delay: i * 0.1, duration: 0.4, ease: [0.22, 1, 0.36, 1] },
  }),
};

export default function CardHover() {
  return (
    <div
      style={{
        padding: 20,
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <p
        style={{
          fontWeight: 700,
          fontSize: 15,
          color: "#111",
          margin: "0 0 16px",
        }}
      >
        🚀 Projects
      </p>
      <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        {PROJECTS.map((p, i) => (
          <motion.div
            key={i}
            custom={i}
            variants={cardVariants}
            initial="hidden"
            animate="visible"
            whileHover={{
              y: -3,
              boxShadow: "0 16px 40px rgba(0,0,0,0.1)",
            }}
            style={{
              background: "white",
              borderRadius: 16,
              overflow: "hidden",
              display: "flex",
              boxShadow: "0 2px 12px rgba(0,0,0,0.06)",
              cursor: "pointer",
            }}
          >
            {/* Gradient accent bar */}
            <motion.div
              whileHover={{ width: 8 }}
              style={{
                width: 5,
                background: p.gradient,
                flexShrink: 0,
                transition: "width 0.2s ease",
              }}
            />

            <div style={{ padding: "14px 16px", flex: 1 }}>
              <div
                style={{
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "space-between",
                  marginBottom: 6,
                }}
              >
                <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <span style={{ fontSize: 20 }}>{p.icon}</span>
                  <p
                    style={{
                      fontWeight: 700,
                      fontSize: 14,
                      color: "#111",
                      margin: 0,
                    }}
                  >
                    {p.title}
                  </p>
                </div>
                <span
                  style={{
                    fontSize: 11,
                    color: "#6b7280",
                    fontWeight: 500,
                    background: "#f3f4f6",
                    padding: "2px 8px",
                    borderRadius: 999,
                  }}
                >
                  {p.stat}
                </span>
              </div>

              <p
                style={{
                  fontSize: 12,
                  color: "#6b7280",
                  margin: "0 0 10px",
                  lineHeight: 1.5,
                }}
              >
                {p.desc}
              </p>

              <div style={{ display: "flex", gap: 5, flexWrap: "wrap" }}>
                {p.tags.map((tag) => (
                  <motion.span
                    key={tag}
                    whileHover={{ scale: 1.08 }}
                    style={{
                      padding: "2px 9px",
                      background: "#f3f4f6",
                      borderRadius: 999,
                      fontSize: 11,
                      fontWeight: 600,
                      color: "#374151",
                    }}
                  >
                    {tag}
                  </motion.span>
                ))}
              </div>
            </div>
          </motion.div>
        ))}
      </div>
    </div>
  );
}


১০. Scroll Progress Bar

কোথায় লাগে: Blog articles, documentation, landing pages, long-form content।

useScroll + useTransform — scroll position থেকে progress bar width তৈরি হয়।

import { useRef } from "react";
import { motion, useScroll, useTransform } from "framer-motion";

function ArticlePage() {
  const containerRef = useRef(null);
  const { scrollYProgress } = useScroll({ container: containerRef });
  const scaleX = useTransform(scrollYProgress, [0, 1], [0, 1]);

  return (
    <div ref={containerRef} style={{ height: "100vh", overflowY: "scroll" }}>
      {/* Sticky progress bar */}
      <div style={{ position: "sticky", top: 0, zIndex: 10 }}>
        <motion.div
          style={{
            height: 3,
            background: "linear-gradient(90deg, #667eea, #764ba2)",
            transformOrigin: "left center",
            scaleX, // 0 → 1 as you scroll
          }}
        />
      </div>
      {/* Long content */}
    </div>
  );
}
import { motion, useScroll, useTransform } from "framer-motion";
import { useRef } from "react";

const SECTIONS = [
  {
    icon: "🚀",
    title: "Hero Section",
    color: "#ede9fe",
    body: "প্রথম screen-এ আপনার মূল message। Strong headline, subtext এবং একটা CTA button রাখুন।",
  },
  {
    icon: "✨",
    title: "Features",
    color: "#dbeafe",
    body: "৩–৬টি key feature। Icon + short description। Grid বা alternate layout ব্যবহার করুন।",
  },
  {
    icon: "💬",
    title: "Social Proof",
    color: "#dcfce7",
    body: "Real user testimonials বা client logos। Trust তৈরি করতে এটা সবচেয়ে কার্যকর।",
  },
  {
    icon: "💰",
    title: "Pricing",
    color: "#fef3c7",
    body: "সর্বোচ্চ ৩টি plan। Most popular plan highlight করুন। Annual/monthly toggle রাখুন।",
  },
  {
    icon: "🎯",
    title: "Call to Action",
    color: "#fce7f3",
    body: "Final conversion section। Strong headline + benefit + button। Simple রাখুন।",
  },
];

export default function ScrollProgress() {
  const containerRef = useRef(null);
  const { scrollYProgress } = useScroll({ container: containerRef });
  const scaleX = useTransform(scrollYProgress, [0, 1], [0, 1]);

  return (
    <div
      style={{
        fontFamily: "system-ui, sans-serif",
        position: "relative",
      }}
    >
      {/* Scroll container */}
      <div
        ref={containerRef}
        style={{
          height: 420,
          overflowY: "scroll",
          scrollbarWidth: "thin",
          scrollbarColor: "#e5e7eb transparent",
        }}
      >
        {/* Sticky header with progress bar */}
        <div
          style={{
            position: "sticky",
            top: 0,
            zIndex: 10,
            background: "rgba(255,255,255,0.9)",
            backdropFilter: "blur(8px)",
            borderBottom: "1px solid #f0f0f0",
          }}
        >
          <div
            style={{
              display: "flex",
              alignItems: "center",
              justifyContent: "space-between",
              padding: "10px 20px",
            }}
          >
            <span style={{ fontWeight: 700, fontSize: 13, color: "#111" }}>
              📄 Landing Page Blueprint
            </span>
            <span style={{ fontSize: 11, color: "#9ca3af" }}>scroll ↓</span>
          </div>
          {/* Progress bar */}
          <motion.div
            style={{
              height: 3,
              background: "linear-gradient(90deg, #667eea, #764ba2, #f093fb)",
              transformOrigin: "left center",
              scaleX,
            }}
          />
        </div>

        {/* Sections */}
        <div style={{ padding: "16px 20px 32px" }}>
          {SECTIONS.map((s, i) => (
            <motion.div
              key={i}
              initial={{ opacity: 0, x: -20 }}
              whileInView={{ opacity: 1, x: 0 }}
              viewport={{ once: true, root: containerRef }}
              transition={{
                delay: 0.05,
                duration: 0.5,
                ease: [0.22, 1, 0.36, 1],
              }}
              whileHover={{ x: 4 }}
              style={{
                marginBottom: 14,
                padding: "16px 18px",
                background: s.color,
                borderRadius: 14,
                cursor: "default",
              }}
            >
              <div
                style={{
                  display: "flex",
                  alignItems: "center",
                  gap: 10,
                  marginBottom: 7,
                }}
              >
                <span style={{ fontSize: 22 }}>{s.icon}</span>
                <p
                  style={{
                    fontWeight: 700,
                    fontSize: 14,
                    color: "#111",
                    margin: 0,
                  }}
                >
                  {String(i + 1).padStart(2, "0")}{s.title}
                </p>
              </div>
              <p
                style={{
                  fontSize: 12,
                  color: "#555",
                  margin: 0,
                  lineHeight: 1.6,
                }}
              >
                {s.body}
              </p>
            </motion.div>
          ))}
        </div>
      </div>
    </div>
  );
}


১১. Counter / Stats Animation

কোথায় লাগে: Stats section, dashboard numbers, achievement counters।

useMotionValue + animate + useInView — viewport-এ এলেই count শুরু হয়।

import { useEffect, useRef } from "react";
import {
  motion,
  useMotionValue,
  useTransform,
  animate,
  useInView,
} from "framer-motion";

function AnimatedCounter({ value }) {
  const count = useMotionValue(0);
  const rounded = useTransform(count, (v) => Math.round(v).toLocaleString());
  const ref = useRef(null);
  const inView = useInView(ref, { once: true });

  useEffect(() => {
    if (!inView) return;
    const ctrl = animate(count, value, { duration: 2.2, ease: "easeOut" });
    return ctrl.stop;
  }, [inView, value]);

  return (
    <span ref={ref}>
      <motion.span>{rounded}</motion.span>
    </span>
  );
}

// ব্যবহার করুন:
<AnimatedCounter value={12400} />; // 0 থেকে 12,400 পর্যন্ত count করবে
import {
  animate,
  motion,
  useInView,
  useMotionValue,
  useTransform,
} from "framer-motion";
import { useEffect, useRef } from "react";

function StatCard({ value, label, prefix, suffix, color }) {
  const count = useMotionValue(0);
  const rounded = useTransform(count, (v) => Math.round(v).toLocaleString());
  const ref = useRef(null);
  const inView = useInView(ref, { once: true });

  useEffect(() => {
    if (!inView) return;
    const ctrl = animate(count, value, { duration: 2.2, ease: "easeOut" });
    return ctrl.stop;
  }, [inView, value]);

  return (
    <motion.div
      ref={ref}
      initial={{ opacity: 0, y: 20 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      whileHover={{ y: -5, boxShadow: "0 16px 40px rgba(0,0,0,0.1)" }}
      style={{
        background: color,
        borderRadius: 16,
        padding: "20px 12px",
        textAlign: "center",
        flex: 1,
      }}
    >
      <p style={{ fontSize: 26, fontWeight: 800, color: "#111", margin: 0 }}>
        {prefix}
        <motion.span>{rounded}</motion.span>
        {suffix}
      </p>
      <p
        style={{
          fontSize: 11,
          color: "#6b7280",
          margin: "4px 0 0",
          fontWeight: 500,
        }}
      >
        {label}
      </p>
    </motion.div>
  );
}

export default function Counter() {
  return (
    <div
      style={{
        padding: 24,
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <p
        style={{
          fontWeight: 700,
          fontSize: 14,
          color: "#111",
          textAlign: "center",
          margin: "0 0 16px",
        }}
      >
        📊 আমাদের সাফল্য
      </p>
      <div style={{ display: "flex", gap: 10 }}>
        <StatCard
          value={12400}
          label="Active Users"
          color="#ede9fe"
          suffix="+"
        />
        <StatCard value={98} label="Satisfaction" color="#dcfce7" suffix="%" />
        <StatCard
          value={5}
          label="Awards"
          color="#fef3c7"
          prefix="🏆"
          suffix=""
        />
      </div>
    </div>
  );
}


১২. Loading Animations (Spinner, Bounce, Pulse)

কোথায় লাগে: API call চলার সময়, image load হওয়ার আগে, lazy loading।

import { motion } from "framer-motion";

// Spinner
<motion.div
  animate={{ rotate: 360 }}
  transition={{ duration: 0.9, repeat: Infinity, ease: "linear" }}
  style={{
    width: 36,
    height: 36,
    borderRadius: "50%",
    border: "3px solid #e5e7eb",
    borderTopColor: "#667eea",
  }}
/>;

// Bouncing dots
{
  [0, 1, 2].map((i) => (
    <motion.div
      key={i}
      animate={{ y: [0, -16, 0] }}
      transition={{
        duration: 0.55,
        repeat: Infinity,
        ease: "easeInOut",
        delay: i * 0.14,
      }}
      style={{
        width: 12,
        height: 12,
        borderRadius: "50%",
        background: "#667eea",
      }}
    />
  ));
}

// Pulse ring (notification bell effect)
<div style={{ position: "relative" }}>
  <motion.div
    animate={{ scale: [1, 2], opacity: [0.5, 0] }}
    transition={{ duration: 1.3, repeat: Infinity, ease: "easeOut" }}
    style={{
      position: "absolute",
      inset: 0,
      borderRadius: "50%",
      background: "#667eea",
    }}
  />
  <div style={{ position: "relative" }}>🔔</div>
</div>;
import { motion } from "framer-motion";

export default function SpringAnimation() {
  return (
    <div
      style={{
        padding: 28,
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        gap: 32,
        fontFamily: "system-ui, sans-serif",
      }}
    >
      {/* Bouncing loader */}
      <div style={{ textAlign: "center" }}>
        <p
          style={{
            color: "#9ca3af",
            fontSize: 11,
            letterSpacing: 1,
            marginBottom: 14,
          }}
        >
          BOUNCING DOTS
        </p>
        <div
          style={{
            display: "flex",
            gap: 8,
            alignItems: "center",
            justifyContent: "center",
          }}
        >
          {["#667eea", "#f093fb", "#4facfe"].map((color, i) => (
            <motion.div
              key={i}
              animate={{ y: [0, -18, 0] }}
              transition={{
                duration: 0.55,
                repeat: Infinity,
                ease: "easeInOut",
                delay: i * 0.14,
              }}
              style={{
                width: 13,
                height: 13,
                borderRadius: "50%",
                background: color,
              }}
            />
          ))}
        </div>
      </div>

      {/* Pulse ring */}
      <div style={{ textAlign: "center" }}>
        <p
          style={{
            color: "#9ca3af",
            fontSize: 11,
            letterSpacing: 1,
            marginBottom: 14,
          }}
        >
          PULSE RING
        </p>
        <div
          style={{
            position: "relative",
            width: 56,
            height: 56,
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
          }}
        >
          <motion.div
            animate={{ scale: [1, 2], opacity: [0.5, 0] }}
            transition={{ duration: 1.3, repeat: Infinity, ease: "easeOut" }}
            style={{
              position: "absolute",
              inset: 0,
              borderRadius: "50%",
              background: "#667eea",
            }}
          />
          <div
            style={{
              width: 56,
              height: 56,
              borderRadius: "50%",
              background: "linear-gradient(135deg, #667eea, #764ba2)",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              fontSize: 22,
              position: "relative",
              color: "white",
            }}
          >
            🔔
          </div>
        </div>
      </div>

      {/* Spinner */}
      <div style={{ textAlign: "center" }}>
        <p
          style={{
            color: "#9ca3af",
            fontSize: 11,
            letterSpacing: 1,
            marginBottom: 14,
          }}
        >
          SPINNER
        </p>
        <motion.div
          animate={{ rotate: 360 }}
          transition={{ duration: 0.9, repeat: Infinity, ease: "linear" }}
          style={{
            width: 36,
            height: 36,
            borderRadius: "50%",
            border: "3.5px solid #e5e7eb",
            borderTopColor: "#667eea",
            margin: "0 auto",
          }}
        />
      </div>
    </div>
  );
}


১৩. Drag Interaction

কোথায় লাগে: Kanban board cards, image reorder, swipe-to-dismiss, carousel।

import { motion } from "framer-motion";

<motion.div
  drag                                        // x আর y দুদিকেই drag
  dragConstraints={{ top: -100, left: -100, right: 100, bottom: 100 }}
  dragElastic={0.1}                          // কতটা টান দেওয়া যাবে
  whileDrag={{ scale: 1.1, zIndex: 10 }}
  style={{ cursor: "grab" }}
>
  Drag me!
</motion.div>

// Only horizontal drag (swipe cards)
<motion.div
  drag="x"
  dragConstraints={{ left: 0, right: 0 }}    // snap back
  onDragEnd={(e, info) => {
    if (info.offset.x > 100) handleSwipeRight();
    if (info.offset.x < -100) handleSwipeLeft();
  }}
>
  Swipe me
</motion.div>
import { motion } from "framer-motion";

const CARDS = [
  {
    icon: "🎯",
    title: "Design",
    gradient: "linear-gradient(135deg, #667eea, #764ba2)",
  },
  {
    icon: "💡",
    title: "Build",
    gradient: "linear-gradient(135deg, #f093fb, #f5576c)",
  },
  {
    icon: "🚀",
    title: "Launch",
    gradient: "linear-gradient(135deg, #4facfe, #00f2fe)",
  },
];

export default function DragExample() {
  return (
    <div
      style={{
        padding: 24,
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        gap: 12,
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <p
        style={{ color: "#9ca3af", fontSize: 12, margin: 0, letterSpacing: 1 }}
      >
        DRAG CARDS
      </p>
      <div style={{ position: "relative", width: 200, height: 160 }}>
        {CARDS.map((card, i) => (
          <motion.div
            key={card.title}
            drag
            dragConstraints={{ top: -80, left: -80, right: 80, bottom: 80 }}
            dragElastic={0.08}
            whileDrag={{
              scale: 1.12,
              zIndex: 10,
              boxShadow: "0 24px 48px rgba(0,0,0,0.25)",
              cursor: "grabbing",
            }}
            whileHover={{ scale: 1.04 }}
            style={{
              position: "absolute",
              top: i * 12,
              left: i * 12,
              width: 148,
              height: 110,
              background: card.gradient,
              borderRadius: 18,
              display: "flex",
              flexDirection: "column",
              alignItems: "center",
              justifyContent: "center",
              cursor: "grab",
              color: "white",
              userSelect: "none",
              boxShadow: "0 8px 24px rgba(0,0,0,0.15)",
            }}
          >
            <span style={{ fontSize: 28, marginBottom: 6 }}>{card.icon}</span>
            <span style={{ fontWeight: 700, fontSize: 14 }}>{card.title}</span>
          </motion.div>
        ))}
      </div>
      <p style={{ color: "#d1d5db", fontSize: 11, margin: 0 }}>
        cards সরান — snap back হবে
      </p>
    </div>
  );
}


Tips: Copy-Paste করার আগে জেনে নিন

সেরা Easing values

// iOS-style spring (সবচেয়ে natural)
ease: [0.22, 1, 0.36, 1]

// Snappy
ease: [0.4, 0, 0.2, 1]

// Bounce effect
type: "spring", stiffness: 400, damping: 25

// Soft spring
type: "spring", stiffness: 200, damping: 20

Performance মাথায় রাখুন

// ✅ GPU-accelerated — এগুলো ব্যবহার করুন
animate={{ opacity, scale, x, y, rotate }}

// ⚠️ Layout-triggering — ব্যবহারে সতর্ক থাকুন
animate={{ width, height, padding, margin }}

// সবসময় will-change hint দিন বড় animation-এ
style={{ willChange: "transform" }}

AnimatePresence-এর সাধারণ ভুল

// ❌ ভুল — key নেই, exit animation হবে না
<AnimatePresence>
  {show && <motion.div animate={...} exit={...} />}
</AnimatePresence>

// ✅ সঠিক
<AnimatePresence>
  {show && <motion.div key="my-div" animate={...} exit={...} />}
</AnimatePresence>

// mode="wait" — আগেরটা exit করার পর নতুনটা enter করবে
<AnimatePresence mode="wait">
  <motion.div key={activeTab} ... />
</AnimatePresence>

আরও জানতে


On this page