# Motion (Framer Motion) Patterns React animation patterns using Motion library (formerly Framer Motion). ## Table of Contents 1. [Setup](#setup) 2. [Basic Animations](#basic-animations) 3. [Scroll Animations](#scroll-animations) 4. [Page Transitions](#page-transitions) 5. [Text Animations](#text-animations) 6. [Gestures](#gestures) 7. [Layout Animations](#layout-animations) 8. [Exit Animations](#exit-animations) ## Setup ```bash npm install motion ``` ```jsx import { motion, useScroll, useTransform, AnimatePresence } from 'motion/react' ``` ## Basic Animations ### Simple Animation ```jsx Content ``` ### Variants Pattern ```jsx const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.1, delayChildren: 0.2 } } } const itemVariants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { duration: 0.5, ease: 'easeOut' } } } function List({ items }) { return ( {items.map(item => ( {item.name} ))} ) } ``` ### While In View ```jsx Animates when scrolled into view ``` ## Scroll Animations ### Scroll Progress ```jsx import { motion, useScroll, useTransform } from 'motion/react' function ScrollProgress() { const { scrollYProgress } = useScroll() return ( ) } ``` ### Parallax Effect ```jsx function ParallaxHero() { const { scrollY } = useScroll() const y = useTransform(scrollY, [0, 500], [0, 150]) const opacity = useTransform(scrollY, [0, 300], [1, 0]) return (
Hero Title
) } ``` ### Element-Based Scroll ```jsx function ScrollSection() { const ref = useRef(null) const { scrollYProgress } = useScroll({ target: ref, offset: ['start end', 'end start'] }) const scale = useTransform(scrollYProgress, [0, 0.5, 1], [0.8, 1, 0.8]) const opacity = useTransform(scrollYProgress, [0, 0.3, 0.7, 1], [0, 1, 1, 0]) return ( Content ) } ``` ### Scroll Velocity ```jsx import { useScroll, useVelocity, useTransform, motion } from 'motion/react' function VelocityText() { const { scrollY } = useScroll() const scrollVelocity = useVelocity(scrollY) const skewY = useTransform(scrollVelocity, [-1000, 0, 1000], [-3, 0, 3]) return ( Velocity Skew ) } ``` ## Page Transitions ### Basic Page Transition ```jsx // layout.jsx import { AnimatePresence } from 'motion/react' function Layout({ children }) { return ( {children} ) } // page.jsx const pageVariants = { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -20 } } function Page() { return ( Content ) } ``` ### Overlay Transition ```jsx const overlayVariants = { initial: { scaleY: 0 }, animate: { scaleY: 1, transition: { duration: 0.5, ease: [0.76, 0, 0.24, 1] } }, exit: { scaleY: 0, transition: { duration: 0.5, ease: [0.76, 0, 0.24, 1], delay: 0.2 } } } function PageTransition({ children }) { return ( <> {children} ) } ``` ### Shared Layout Animation ```jsx function CardGrid({ items, selectedId, setSelectedId }) { return ( <>
{items.map(item => ( setSelectedId(item.id)} > {item.title} ))}
{selectedId && ( setSelectedId(null)} > {items.find(i => i.id === selectedId).title} )} ) } ``` ## Text Animations ### Character-by-Character Reveal ```jsx function AnimatedText({ text }) { const chars = text.split('') const container = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.02 } } } const child = { hidden: { opacity: 0, y: 50, rotateX: -90 }, visible: { opacity: 1, y: 0, rotateX: 0, transition: { type: 'spring', damping: 12 } } } return ( {chars.map((char, i) => ( {char === ' ' ? '\u00A0' : char} ))} ) } ``` ### Word-by-Word Animation ```jsx function AnimatedWords({ text }) { const words = text.split(' ') return ( {words.map((word, i) => ( {word}{' '} ))} ) } ``` ### Line Mask Reveal ```jsx function MaskReveal({ children }) { return (
{children}
) } ``` ## Gestures ### Magnetic Button ```jsx function MagneticButton({ children }) { const ref = useRef(null) const [position, setPosition] = useState({ x: 0, y: 0 }) const handleMouse = (e) => { const { clientX, clientY } = e const { left, top, width, height } = ref.current.getBoundingClientRect() const x = (clientX - left - width / 2) * 0.3 const y = (clientY - top - height / 2) * 0.3 setPosition({ x, y }) } const reset = () => setPosition({ x: 0, y: 0 }) return ( {children} ) } ``` ### Hover Effects ```jsx function HoverCard() { return ( ) } ``` ### Drag ```jsx function DraggableCard() { return ( Drag me ) } ``` ## Layout Animations ### Reorder List ```jsx import { Reorder } from 'motion/react' function ReorderList() { const [items, setItems] = useState([1, 2, 3, 4]) return ( {items.map(item => ( {item} ))} ) } ``` ### Accordion ```jsx function Accordion({ title, children }) { const [isOpen, setIsOpen] = useState(false) return (
setIsOpen(!isOpen)}> {title} {isOpen && ( {children} )}
) } ``` ## Exit Animations ### Exit with AnimatePresence ```jsx function Modal({ isOpen, onClose, children }) { return ( {isOpen && ( <> {children} )} ) } ``` ## Easing Reference ```jsx // Custom cubic bezier (recommended) transition: { ease: [0.76, 0, 0.24, 1] } // ease-in-out transition: { ease: [0.16, 1, 0.3, 1] } // ease-out (smooth) transition: { ease: [0.87, 0, 0.13, 1] } // dramatic // Spring (for bouncy feel) transition: { type: 'spring', stiffness: 300, damping: 20 } // Tween (for precise control) transition: { type: 'tween', duration: 0.5, ease: 'easeOut' } ``` ## Performance Tips 1. Use `layout` prop sparingly - it can be expensive 2. Avoid animating `width`/`height` - use `scale` instead 3. Use `will-change: transform` via CSS for heavy animations 4. Use `layoutId` only when necessary 5. Memoize variants objects 6. Use `useReducedMotion` hook for accessibility ```jsx import { useReducedMotion } from 'motion/react' function Component() { const shouldReduceMotion = useReducedMotion() return ( ) } ```