Motion (Framer Motion) React animation library. Use for drag-and-drop, scroll animations, gestures, SVG morphing, or encountering bundle size, complex transitions, spring physics errors.
84
Does it follow best practices?
If you maintain this skill, you can automatically optimize it using the tessl CLI to improve its score:
npx tessl skill review --optimize ./path/to/skillValidation for skill structure
Motion (package: motion, formerly framer-motion) is the industry-standard React animation library used in production by thousands of applications. With 30,200+ GitHub stars and 300+ official examples, it provides a declarative API for creating sophisticated animations with minimal code.
Key Capabilities:
Production Tested: React 19, Next.js 15, Vite 6, Tailwind v4
Complex Interactions:
Scroll-Based Animations:
Layout Transitions:
Advanced Features:
Bundle Optimization:
auto-animate instead: 3.28 KB vs 34 KB)framer-motion v12.23.24 workaround - see Known Issues)bun add motion # preferred
# or: npm install motion
# or: yarn add motionCurrent Version: 12.23.24 (verified 2025-11-07)
Alternative for Cloudflare Workers:
# Use framer-motion if deploying to Cloudflare Workers
bun add framer-motion
# or: npm install framer-motionmotion component: ~34 KB minified+gzippedLazyMotion + m component: ~4.6 KBuseAnimate mini: 2.3 KB (smallest React animation library)useAnimate hybrid: 17 KBmotion ComponentTransform any HTML/SVG element into an animatable component:
import { motion } from "motion/react"
// Basic animation
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
Content fades in and slides up
</motion.div>
// Gesture controls
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
>
Click me
</motion.button>Props:
initial: Starting state (object or variant name)animate: Target state (object or variant name)exit: Unmounting state (requires AnimatePresence)transition: Timing/easing configurationwhileHover, whileTap, whileFocus: Gesture stateswhileInView: Viewport-triggered animationdrag: Enable dragging ("x", "y", or true for both)layout: Enable FLIP layout animationsNamed animation states that propagate through component tree:
const variants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
}
<motion.div variants={variants} initial="hidden" animate="visible">
Content
</motion.div>For advanced orchestration (staggerChildren, delayChildren, dynamic variants), load references/core-concepts-deep-dive.md.
Enables animations when components unmount:
import { AnimatePresence } from "motion/react"
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Modal content
</motion.div>
)}
</AnimatePresence>Critical Rules:
key propsCommon Mistake (exit animation won't play):
// ❌ Wrong - AnimatePresence unmounts with condition
{isVisible && (
<AnimatePresence>
<motion.div>Content</motion.div>
</AnimatePresence>
)}
// ✅ Correct - AnimatePresence stays mounted
<AnimatePresence>
{isVisible && <motion.div key="unique">Content</motion.div>}
</AnimatePresence>Automatically animate layout changes:
<motion.div layout>
{isExpanded ? <FullContent /> : <Summary />}
</motion.div>Special props: layoutId (shared element transitions), layoutScroll (scrollable containers), layoutRoot (fixed positioning).
For advanced patterns (LayoutGroup, layoutId orchestration), load references/core-concepts-deep-dive.md.
// Viewport-triggered
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
>
Fades in when entering viewport
</motion.div>
// Scroll-linked (parallax)
import { useScroll, useTransform } from "motion/react"
const { scrollYProgress } = useScroll()
const y = useTransform(scrollYProgress, [0, 1], [0, -300])
<motion.div style={{ y }}>Parallax effect</motion.div>For advanced scroll patterns (useScroll offsets, useTransform easing, parallax layers), load references/core-concepts-deep-dive.md.
<motion.div drag="x" dragConstraints={{ left: -200, right: 200 }}>
Drag me
</motion.div>Available: whileHover, whileTap, whileFocus, whileDrag, whileInView, drag.
For advanced drag controls (momentum, elastic, event handlers), load references/core-concepts-deep-dive.md.
<motion.div
animate={{ x: 100 }}
transition={{ type: "spring", stiffness: 100, damping: 10 }}
/>Common presets: Bouncy { stiffness: 300, damping: 10 }, Smooth { stiffness: 100, damping: 20 }.
For spring tuning (mass, visualizer, presets), load references/core-concepts-deep-dive.md.
Vite: bun add motion → import { motion } from "motion/react" (works out of the box)
Next.js App Router: Requires "use client" directive or client component wrapper
"use client"
import { motion } from "motion/react"Tailwind: ⚠️ Remove transition-* classes (causes conflicts with Motion animations)
Cloudflare Workers: Use framer-motion v12.23.24 instead (Motion has Wrangler build issues)
For complete integration guides (Next.js patterns, SSR, framework-specific issues), load references/nextjs-integration.md.
Bundle Size: Use LazyMotion (34 KB → 4.6 KB):
import { LazyMotion, domAnimation, m } from "motion/react"
<LazyMotion features={domAnimation}>
<m.div>Only 4.6 KB!</m.div>
</LazyMotion>Large Lists: Use virtualization (react-window, react-virtuoso) for 50+ animated items.
For complete optimization guide (hardware acceleration, memory profiling, production benchmarks), load references/performance-optimization.md.
Respect prefers-reduced-motion:
import { MotionConfig } from "motion/react"
<MotionConfig reducedMotion="user">
<App />
</MotionConfig>Keyboard Support: Use whileFocus for keyboard-triggered animations.
<motion.button whileFocus={{ scale: 1.1 }} tabIndex={0}>
Keyboard accessible
</motion.button>For complete accessibility guide (ARIA patterns, screen readers, AnimatePresence workaround, testing), load references/accessibility-guide.md.
Modal Dialog (AnimatePresence + backdrop):
<AnimatePresence>
{isOpen && (
<motion.dialog exit={{ opacity: 0 }}>Content</motion.dialog>
)}
</AnimatePresence>Accordion (height animation):
<motion.div animate={{ height: isOpen ? "auto" : 0 }}>
Content
</motion.div>For 15+ production patterns (carousel, tabs, scroll reveal, parallax, notifications), load references/common-patterns.md.
Symptom: Components disappear instantly without exit animation.
Solution: AnimatePresence must stay mounted, wrap the conditional (not be wrapped by it):
// ❌ Wrong
{isVisible && <AnimatePresence><motion.div>Content</motion.div></AnimatePresence>}
// ✅ Correct
<AnimatePresence>
{isVisible && <motion.div key="unique">Content</motion.div>}
</AnimatePresence>Symptom: Build fails with "motion is not defined" or SSR errors.
Solution: Add "use client" directive:
"use client"
import { motion } from "motion/react"Symptom: Animations stutter or don't work.
Solution: Remove transition-* classes (Motion overrides CSS transitions):
// ❌ Wrong: <motion.div className="transition-all" animate={{ x: 100 }} />
// ✅ Correct: <motion.div animate={{ x: 100 }} />Symptom: Wrangler build fails when using motion package.
Solution: Use framer-motion v12.23.24 instead (GitHub issue #2918):
bun add framer-motion # Same API, works with WorkersSymptom: 50-100+ animated items cause severe slowdown.
Solution: Use virtualization (react-window, react-virtuoso).
For 5+ additional issues (layoutScroll, layoutRoot, AnimatePresence + layoutId), load references/nextjs-integration.md or references/core-concepts-deep-dive.md.
Claude should load these references based on user needs:
references/core-concepts-deep-dive.md when:references/performance-optimization.md when:references/nextjs-integration.md when:references/accessibility-guide.md when:references/common-patterns.md when:references/motion-vs-auto-animate.md when:This skill includes 5 production-ready templates in the templates/ directory:
Copy templates into your project and customize as needed.
This skill includes 4 comprehensive reference guides:
See references/ directory for detailed guides.
This skill includes 2 automation scripts:
See scripts/ directory for automation tools.
Related Skills: auto-animate (simple lists), tailwind-v4-shadcn (styling), nextjs (App Router), cloudflare-worker-base
Motion vs AutoAnimate: Load references/motion-vs-auto-animate.md for detailed comparison.
Token Savings: ~83% (30k → 5k tokens) | Error Prevention: 100% (29+ errors) | Time Savings: ~85% (2-3 hrs → 20-30 min)
| Package | Version | Status |
|---|---|---|
| motion | 12.23.24 | ✅ Latest stable |
| framer-motion | 12.23.24 | ✅ Alternative for Cloudflare |
| react | 19.2.0 | ✅ Latest stable |
| vite | 6.0.0 | ✅ Latest stable |
Found an issue or have a suggestion?
Production Tested: ✅ React 19 + Next.js 15 + Vite 6 + Tailwind v4
Token Savings: ~83%
Error Prevention: 100% (29+ documented errors prevented)
Bundle Size: 2.3 KB (mini) - 34 KB (full), optimizable to 4.6 KB with LazyMotion
Accessibility: MotionConfig reducedMotion support
Ready to use! Install with ./scripts/install-skill.sh motion
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.