Framer Motion Skill

SkillAI & models

Animation library for React. Declarative, physics-based, gesture-aware.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Framer Motion Skill skill

About this capability

Specialized agents, MCP servers, skills, and a visual dashboard for Claude Code. MIT.

What this skill tells your AI

The instructions your AI receives, as published by claude-dev-suite/claude-dev-suite in skills/animation/framer-motion/SKILL.md and read by ahel’s review.

Animation library for React. Declarative, physics-based, gesture-aware.

Install

npm install framer-motion

Core API

APIPurpose
motion.divAnimatable element
animate propTarget state
initial propStarting state
exit propUnmount state (requires AnimatePresence)
variantsNamed animation states, enables orchestration
transitionTiming, easing, spring config
whileHover / whileTap / whileFocusGesture states
dragDrag gesture with constraints
layout / layoutIdFLIP layout animation
AnimatePresenceAnimates unmounting components
useScrollScroll progress values
useTransformMap one motion value to another
useSpringSpring-based motion value
useInViewObserve element entering viewport
useAnimationImperative animation control

Variants & Orchestration

const container = {
  hidden: {},
  visible: { transition: { staggerChildren: 0.08, delayChildren: 0.1 } }
};
const item = {
  hidden: { opacity: 0, y: 24 },
  visible: { opacity: 1, y: 0, transition: { duration: 0.4, ease: "easeOut" } }
};

<motion.ul variants={container} initial="hidden" whileInView="visible" viewport={{ once: true }}>
  {items.map(i => <motion.li key={i} variants={item}>{i}</motion.li>)}
</motion.ul>

AnimatePresence

<AnimatePresence mode="wait">
  {isVisible && (
    <motion.div
      key="unique-key"
      initial={{ opacity: 0, y: 10 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, y: -10 }}
      transition={{ duration: 0.2 }}
    />
  )}
</AnimatePresence>

mode="wait" — exits finish before new element enters. mode="popLayout" — exiting element pops out of layout flow.

Layout Animation (FLIP)

// Automatic smooth repositioning
<motion.div layout />

// Shared element transition across routes/conditions
<motion.div layoutId="card-thumbnail" />  // source
<motion.div layoutId="card-thumbnail" />  // destination — auto-animates

Scroll-linked

const { scrollYProgress } = useScroll({ target: ref, offset: ["start end", "end start"] });
const y = useTransform(scrollYProgress, [0, 1], [0, -100]);
const opacity = useTransform(scrollYProgress, [0, 0.3, 1], [0, 1, 0]);

Spring Config

// Bouncy entrance
transition={{ type: "spring", stiffness: 400, damping: 20 }

// Gentle
transition={{ type: "spring", stiffness: 100, damping: 30 }

// No bounce
transition={{ type: "spring", stiffness: 300, damping: 50 }

Gestures

<motion.button
  whileHover={{ scale: 1.05 }}
  whileTap={{ scale: 0.97 }}
  transition={{ type: "spring", stiffness: 400, damping: 20 }}
>
  Click me
</motion.button>

// Drag with constraints
<motion.div
  drag
  dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
  dragElastic={0.2}
/>

Imperative Control

const controls = useAnimation();

await controls.start({ opacity: 1, y: 0, transition: { duration: 0.4 } });
controls.stop();

<motion.div animate={controls} initial={{ opacity: 0, y: 20 }} />

Reduced Motion

import { useReducedMotion } from "framer-motion";

function AnimatedCard() {
  const reduced = useReducedMotion();
  return (
    <motion.div
      initial={{ opacity: 0, y: reduced ? 0 : 20 }}
      animate={{ opacity: 1, y: 0 }}
    />
  );
}

Common Patterns

// Stagger list reveal on scroll
// Page transition wrapper
// Accordion with layout animation
// Drag-and-drop with layoutId
// Hero → detail shared element

Pitfalls

  • exit only works inside <AnimatePresence> — wrap the closest conditional parent
  • layout on elements that change children count can cause jumps — use layoutId instead
  • Spring animations ignore duration — use stiffness/damping
  • useScroll without target tracks window scroll
  • Always set viewport={{ once: true }} for entry animations to avoid re-triggering on scroll-up

Signals

GitHub stars
33
Forks
6
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
framer-motion-claude-dev-suite
Source
github.com/claude-dev-suite/claude-dev-suite