Framer Motion
Overview
Framer Motion is the production animation library for React. Declarative animations via props, spring physics, layout animations, gestures (drag, hover, tap), scroll-triggered effects, and shared layout transitions. Used by Vercel, Linear, Raycast.
Instructions
Step 1: Basic Animations
import { motion } from 'framer-motion'
// Animate on mount
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: 'easeOut' }}
>
<h1>Hello</h1>
</motion.div>
// Animate on hover and tap
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
>
Click me
</motion.button>
// Exit animations (requires AnimatePresence)
import { AnimatePresence } from 'framer-motion'
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
/>
)}
</AnimatePresence>
Step 2: Layout Animations
// Automatic layout animation — element smoothly moves when layout changes
<motion.div layout>
{items.map(item => (
<motion.div key={item.id} layout>
{item.title}
</motion.div>
))}
</motion.div>
// Shared layout animation — element transitions between two positions
<motion.div layoutId={`card-${id}`}>
{isExpanded ? <ExpandedCard /> : <CompactCard />}
</motion.div>
Step 3: Scroll Animations
import { motion, useScroll, useTransform } from 'framer-motion'
function ParallaxHero() {
const { scrollYProgress } = useScroll()
const y = useTransform(scrollYProgress, [0, 1], [0, -200])
const opacity = useTransform(scrollYProgress, [0, 0.5], [1, 0])
return (
<motion.div style={{ y, opacity }}>
<h1>Scroll to reveal</h1>
</motion.div>
)
}
// Animate when element enters viewport
function FadeInOnScroll({ children }) {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-100px' }}
transition={{ duration: 0.6 }}
>
{children}
</motion.div>
)
}
Step 4: Staggered Children
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.id} variants={item}>{i.name}</motion.li>
))}
</motion.ul>
Guidelines
- Use
type: 'spring'for natural-feeling animations — avoid linear easing. layoutIdcreates shared element transitions (like iOS hero animations).AnimatePresenceis required for exit animations — wrap conditional renders.whileInViewwithviewport: { once: true }for scroll-triggered animations.- Framer Motion adds ~30KB to bundle. For simple animations, consider CSS transitions.