প্রতিদিনের React app-এ যে animations লাগে — modal, toast, form, tab, card, scroll — সব একজায়গায়। Copy করুন, paste করুন, কাজ করুন।
আমি প্রতিদিন React app বানাই। আর প্রতিদিনই animation দরকার হয় —
button press করলে feedback চাই, modal খুললে smooth হোক, form submit হলে ইউজার বুঝুক কী হলো।
এই পুরো doc-টা আমার নিজের copy-paste library ।
আপনার জন্যও তাই। কোড দেখুন, বুঝুন, নিজের app-এ ব্যবহার করুন।
npm install framer-motion
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 দিয়ে যা চাই তাই বানানো যাবে।
কোথায় লাগে: Hero section, page title, welcome screen, dashboard cards।
যেকোনো element mount হওয়ার সময় smooth করে আনতে initial → animate ব্যবহার করুন।
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 /> ;
}
কোথায় লাগে: 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, 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 >
{ }
< 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 >
{ }
< 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 >
{ }
< 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 >
) ;
}
কোথায় লাগে: 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 >
) ;
}
কোথায় লাগে: 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" ,
} }
>
{ }
< 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 >
{ }
< 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 >
{ }
< 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 >
) ;
}
কোথায় লাগে: 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 >;
দুটো জিনিস মাথায় রাখুন:
Backdrop আর Modal-এ আলাদা key দিন
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 && (
< >
{ }
< 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 ,
} }
/>
{ }
< 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 >
) ;
}
কোথায় লাগে: 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 >
{ }
< 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 >
{ }
< 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 >
) ;
}
কোথায় লাগে: 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" ) ;
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" >
{ }
{ 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 >
{ }
< 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 >
) }
{ }
{ 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 >
) }
{ }
{ 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 >
) ;
}
কোথায় লাগে: 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" ,
} }
>
{ }
< 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 >
) ;
}
কোথায় লাগে: 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" ,
} }
>
{ }
< div
ref ={ containerRef }
style ={ {
height : 420 ,
overflowY : "scroll" ,
scrollbarWidth : "thin" ,
scrollbarColor : "#e5e7eb transparent" ,
} }
>
{ }
< 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 >
{ }
< motion .div
style ={ {
height : 3 ,
background : "linear-gradient(90deg, #667eea, #764ba2, #f093fb)" ,
transformOrigin : "left center" ,
scaleX ,
} }
/>
</ div >
{ }
< 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 >
) ;
}
কোথায় লাগে: 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 >
) ;
}
কোথায় লাগে: 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" ,
} }
>
{ }
< 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 >
{ }
< 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 >
{ }
< 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 >
) ;
}
কোথায় লাগে: 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 >
) ;
}
// 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
// ✅ GPU-accelerated — এগুলো ব্যবহার করুন
animate = {{ opacity, scale, x, y, rotate }}
// ⚠️ Layout-triggering — ব্যবহারে সতর্ক থাকুন
animate = {{ width, height, padding, margin }}
// সবসময় will-change hint দিন বড় animation-এ
style = {{ willChange : "transform" }}
// ❌ ভুল — 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 >
🚀 React Bangla কমিউনিটি যদি আপনি React , Next.js , Redux , TypeScript এর মতো মর্ডান ফ্রন্টএন্ড টেকনোলজিতে আগ্রহী হন, তাহলে আমাদের Discord সার্ভার এ জয়েন করুন।
🎉 এখনই জয়েন করুন