Agent Skills

framer-motion

Framer Motion / Motion sub-skill - AnimatePresence, layout animations, gestures, motion values.

Install

npx skills add https://github.com/athevon/genjutsu --skill framer-motion
SKILL.md

Version-sensitive. Every API name, SDK gate and browser-support claim below was verified on 2026-09-08 against primary sources. What against, and when, is in _jutsu/VERSIONS.md. If that date is old, re-verify before acting on a version number.

Framer Motion - Sub-skill

Two package names, one library. Framer Motion was renamed to Motion. motion and framer-motion both publish the same version (13.2.0 as of 2026-09-08): motion declares "framer-motion": "^13.2.0" as a dependency and motion/react re-exports it. Neither is broken, and framer-motion is still the one most installed projects have.

Read package.json and follow what is there. Do not migrate a project from one to the other unless the user asks - that is Iron Rule 8 in cast / 10 in paint.

In package.json Import from Install line
motion "motion/react" npm install motion
framer-motion "framer-motion" npm install framer-motion
both whichever the file you are editing already imports; say the project is mid-migration -

Every API in this skill is identical across the two names at v13. Where a symbol is newer than v11, it is flagged inline. Peers: react / react-dom ^18 || ^19.

When to use Framer Motion vs alternatives

Criteria Framer Motion GSAP Native CSS
Layout animations Excellent (layoutId) Manual Impossible
Exit animations AnimatePresence Timeline reverse Limited (display)
Gestures (drag, hover) Native, declarative Draggable plugin Basic
Scroll-driven useScroll + useTransform ScrollTrigger (more powerful) scroll-timeline
Complex orchestration Variants + propagation Timeline (more flexible) @keyframes
Bundle size ~50kb tree-shaken ~30kb core 0kb
React integration Native, component-first Refs + useGSAP className toggle

Rule: Framer Motion for React UI interactions (modals, toasts, reorder, shared layout). GSAP for complex timelines, cinematic scroll-driven, SVG morphing.

AnimatePresence - Exit animations

<AnimatePresence mode="wait">
  {isVisible && (
    <motion.div
      key="unique-key"        // REQUIRED - identifies the component
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    />
  )}
</AnimatePresence>
  • mode="wait" - waits for exit to finish before enter (page transitions)
  • mode="sync" - exit and enter simultaneously
  • mode="popLayout" - removes from flow immediately (good for lists)
  • onExitComplete - callback when all exit animations are finished

Layout animations

// Shared layout - the element "slides" between two positions
<motion.div layoutId="highlight" className={activeTab === id ? "active" : ""} />

// Auto layout - animates position/size when layout changes
<motion.div layout>
  {isExpanded && <motion.p layout>Additional content</motion.p>}
</motion.div>

// layout="position" - animates position only (not size)
// layout="size" - animates size only
// layout="preserve-aspect" - preserves the ratio during the transition

Variants - Propagation and orchestration

import { stagger } from "motion/react";

const container = {
  hidden: { opacity: 0 },
  show: {
    opacity: 1,
    transition: {
      // staggerChildren + staggerDirection are DEPRECATED (Motion 12.22, Jul 2025).
      // Pass a stagger() function to delayChildren instead.
      delayChildren: stagger(0.08, { startDelay: 0.2 }),
      // reverse order: stagger(0.08, { from: "last" })
      // also available: from: "first" | "center" | "last" | index, and ease
    },
  },
};

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} />
  ))}
</motion.ul>

Variants automatically propagate to motion children - no need for initial/animate on children.

Gestures

<motion.div
  whileHover={{ scale: 1.05 }}
  whileTap={{ scale: 0.95 }}
  whileFocus={{ borderColor: "#3b82f6" }}
  // Drag
  drag               // true = x+y, "x" = horizontal only, "y" = vertical only
  dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
  dragElastic={0.2}  // 0 = rigid, 1 = free (default 0.35)
  dragSnapToOrigin   // returns to initial position
  onDragEnd={(e, info) => {
    if (info.offset.x > 100) handleSwipe("right");
  }}
/>

Motion values - Reactive without re-render

const x = useMotionValue(0);
const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0]);
const background = useTransform(x, [-200, 200], ["#ff0000", "#00ff00"]);

// Spring-based smoothing
const smoothX = useSpring(x, { stiffness: 300, damping: 30 });

// Scroll tracking
const { scrollY, scrollYProgress } = useScroll();
const parallaxY = useTransform(scrollYProgress, [0, 1], [0, -300]);

// Element-scoped scroll
const ref = useRef(null);
const { scrollYProgress } = useScroll({
  target: ref,
  offset: ["start end", "end start"],
});

Motion values do NOT trigger React re-renders - they update the DOM directly via style.

Do Not

Do not use motion with styled-components / Emotion without isValidProp (v13+)

Motion 13.0 removed @emotion/is-prop-valid as an optional dependency. Without explicit injection, motion-only props leak onto the DOM.

import isPropValid from "@emotion/is-prop-valid";
import { MotionConfig } from "motion/react";

<MotionConfig isValidProp={isPropValid}>
  <App />
</MotionConfig>

Or reverse the composition so the styling library owns prop forwarding: const MotionDiv = motion.create(StyledDiv).

Do not setState in callbacks without a guard

// BAD - infinite re-render if animate depends on state
onUpdate={(latest) => setPosition(latest.x)}

// GOOD - guard or useMotionValueEvent
const x = useMotionValue(0);
useMotionValueEvent(x, "change", (latest) => {
  if (latest > threshold) onThresholdReached();
});

Do not use layout animation without a stable key

// BAD - key changes every render, breaks layout tracking
<motion.div layout key={Math.random()} />

// GOOD - stable key derived from data
<motion.div layout key={item.id} />

Do not forget the unique key on AnimatePresence

// BAD - no key, exit animation does not trigger
<AnimatePresence>
  {isOpen && <motion.div exit={{ opacity: 0 }} />}
</AnimatePresence>

// GOOD - unique key for each conditional child
<AnimatePresence>
  {isOpen && <motion.div key="modal" exit={{ opacity: 0 }} />}
</AnimatePresence>

Do not wrap an already animated component with motion.div

// BAD - double animation, transform conflicts
<motion.div animate={{ x: 100 }}>
  <motion.div animate={{ x: -50 }}>Content</motion.div>
</motion.div>

// GOOD - single animation level per transform axis
<motion.div animate={{ x: 100 }}>
  <motion.div animate={{ opacity: 0.5 }}>Content</motion.div>
</motion.div>

// GOOD - use variants to coordinate parent/child
<motion.div variants={parent} animate="active">
  <motion.div variants={child} />
</motion.div>

Related skills

vercel-react-best-practicesvercel-labs751KReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.vercel-composition-patternsvercel-labs361KReact composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.vercel-react-view-transitionsvercel-labs135KGuide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions viewpick-ui-libraryemilkowalski124KPick the right library for a given frontend task from a curated, opinionated list — numbers, OTP inputs, charts, command menus, virtualization, drag and drop, toasts, state, styling, and more. Only runs when explicitly invoked; it does not trigger on its own.

Search skills and MCP servers

Fuzzy search across 23,137 skills and servers