Framer Motion Skill
Production-ready animations for React applications.
Quick Start
Installation
npm install framer-motion
or
pnpm add framer-motion
Basic Usage
import { motion } from "framer-motion";
// Simple animation <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5 }}
Content </motion.div>
Core Concepts
Concept Guide
Motion Component reference/motion-component.md
Variants reference/variants.md
Gestures reference/gestures.md
Hooks reference/hooks.md
Examples
Pattern Guide
Page Transitions examples/page-transitions.md
List Animations examples/list-animations.md
Scroll Animations examples/scroll-animations.md
Micro-interactions examples/micro-interactions.md
Templates
Template Purpose
templates/page-transition.tsx Page transition wrapper
templates/animated-list.tsx Animated list component
Quick Reference
Basic Animation
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.3 }}
Content </motion.div>
Hover & Tap
<motion.button whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} transition={{ type: "spring", stiffness: 400, damping: 17 }}
Click me </motion.button>
Variants
const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.1 } } };
const item = { hidden: { opacity: 0, y: 20 }, show: { opacity: 1, y: 0 } };
<motion.ul variants={container} initial="hidden" animate="show"> {items.map(i => ( <motion.li key={i} variants={item}>{i}</motion.li> ))} </motion.ul>
AnimatePresence (Exit Animations)
import { AnimatePresence, motion } from "framer-motion";
<AnimatePresence mode="wait"> {isVisible && ( <motion.div key="modal" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} > Modal content </motion.div> )} </AnimatePresence>
Scroll Trigger
<motion.div initial={{ opacity: 0, y: 50 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: "-100px" }} transition={{ duration: 0.5 }}
Animates when scrolled into view </motion.div>
Drag
<motion.div drag dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }} dragElastic={0.1}
Drag me </motion.div>
Layout Animation
<motion.div layout layoutId="shared-element"> Content that animates when layout changes </motion.div>
Transition Types
// Tween (default) transition={{ duration: 0.3, ease: "easeOut" }}
// Spring transition={{ type: "spring", stiffness: 300, damping: 20 }}
// Spring presets transition={{ type: "spring", bounce: 0.25 }}
// Inertia (for drag) transition={{ type: "inertia", velocity: 50 }}
Easing Functions
// Built-in easings ease: "linear" ease: "easeIn" ease: "easeOut" ease: "easeInOut" ease: "circIn" ease: "circOut" ease: "circInOut" ease: "backIn" ease: "backOut" ease: "backInOut"
// Custom cubic-bezier ease: [0.17, 0.67, 0.83, 0.67]
Reduced Motion
Always respect user preferences:
import { motion, useReducedMotion } from "framer-motion";
function Component() { const prefersReducedMotion = useReducedMotion();
return ( <motion.div initial={{ opacity: 0, y: prefersReducedMotion ? 0 : 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: prefersReducedMotion ? 0 : 0.3 }} > Respects motion preferences </motion.div> ); }
// Or use media query const variants = { initial: { opacity: 0 }, animate: { opacity: 1 }, };
<motion.div variants={variants} initial="initial" animate="animate" className="motion-reduce:transition-none"
Common Patterns
Fade In Up
const fadeInUp = { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.4 } };
<motion.div {...fadeInUp}>Content</motion.div>
Staggered List
const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.1, delayChildren: 0.2 } } };
const item = { hidden: { opacity: 0, x: -20 }, show: { opacity: 1, x: 0 } };
Modal
<AnimatePresence> {isOpen && ( <> {/* Backdrop /} <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="fixed inset-0 bg-black/50" onClick={onClose} /> {/ Modal */} <motion.div initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} className="fixed inset-x-4 top-1/2 -translate-y-1/2 ..." > Modal content </motion.div> </> )} </AnimatePresence>
Accordion
<motion.div initial={false} animate={{ height: isOpen ? "auto" : 0 }} transition={{ duration: 0.3, ease: "easeInOut" }} className="overflow-hidden"
<div className="p-4">Accordion content</div> </motion.div>
Best Practices
-
Use variants: Cleaner code, easier orchestration
-
Respect reduced motion: Always check useReducedMotion
-
Use layout sparingly: Can be expensive, use only when needed
-
Exit animations: Wrap with AnimatePresence
-
Spring for interactions: More natural feel for hover/tap
-
Tween for page transitions: More predictable timing
-
GPU-accelerated properties: Prefer opacity , scale , x , y over width , height