skillmake
← marketplace
creatorslibrarysha:f9edcb7a3bcd5e3amanual

framer-motion

Use when adding animations, transitions, gestures, or scroll effects to React/Next.js — Motion (formerly Framer Motion) patterns: motion.div, AnimatePresence, layout/layoutId, springs, variants, whileInView.

One-line install
curl --create-dirs -fsSL https://skillmake.xyz/i/framer-motion -o ~/.claude/skills/framer-motion/SKILL.md

The hash above pins this exact content. The file we serve at /api/marketplace/framer-motion-f9edcb7a/raw always matches sha:f9edcb7a3bcd5e3a.

6,443 chars · ~1,611 tokens
---
name: framer-motion
description: "Use when adding animations, transitions, gestures, or scroll effects to React/Next.js — Motion (formerly Framer Motion) patterns: motion.div, AnimatePresence, layout/layoutId, springs, variants, whileInView."
source: https://motion.dev/docs
generated: 2026-05-09T00:37:56.376Z
category: library
audience: creators
---

## When to use

- Animating React components declaratively with initial/animate/exit + transition props
- Orchestrating page transitions, list reorders, and shared-element layout animations
- Scroll-triggered reveals (whileInView), parallax, and pinned scroll sections
- Gesture handling — drag, hover, tap, pan — with spring physics instead of CSS keyframes

## Key concepts

### motion components

motion.div / motion.span / motion(Component) replace plain DOM nodes and accept animate, initial, exit, whileHover, whileTap, whileInView, drag, layout, and transition props. They render as the underlying element — no extra wrapper — and only opt into animation when those props are present.

### AnimatePresence

Wraps conditional renders so unmounting children can run an exit animation before they leave the tree. Only animates DIRECT children; intermediate wrappers between AnimatePresence and the conditionally-mounted node break exit animations. Each conditional child needs a stable key.

### layout / layoutId

Pass `layout` to auto-animate any size/position change a component undergoes. Pass `layoutId="shared-id"` on two components in different parts of the tree to morph between them as one unmounts and the other mounts — the foundation of shared-element transitions.

### whileInView + viewport

Scroll-triggered animation without manually wiring IntersectionObserver. Pass viewport={{ once: true }} to fire the animation only the first time the element enters; otherwise it replays on every re-entry.

### spring vs tween

Two transition types: tween is duration-based (use for choreographed reveals, page transitions); spring is physics-based via stiffness/damping/mass (use for gestures, drag release, organic motion). Default to spring for natural-feeling interaction; tween for timed sequences.

### variants + stagger

Variants are named animation states attached to a parent that propagate to children, enabling orchestrated sequences. transition.staggerChildren on the parent variant offsets each child's animation in time without per-child math.

### motion/react vs framer-motion

The package was renamed: new code should import from `motion/react` (or `motion/react-client` for Next.js client boundary). The legacy `framer-motion` import still works for back-compat but isn't where new APIs land.

## API reference

```
import { motion } from 'motion/react'
```

Preferred import path. Use 'motion/react-client' inside Next.js Server Components to mark the client boundary; 'framer-motion' still works as a legacy alias.

```
import { motion } from 'motion/react';

export function Card() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ type: 'spring', stiffness: 200, damping: 20 }}
    />
  );
}
```

```
AnimatePresence with conditional render
```

Wrap exits so unmounting children animate out. Each child needs a stable key. Use mode='wait' to fully unmount the previous child before mounting the next.

```
import { AnimatePresence, motion } from 'motion/react';

<AnimatePresence mode="wait">
  {open && (
    <motion.div
      key="panel"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    />
  )}
</AnimatePresence>
```

```
Variants + staggerChildren
```

Define named states on a parent and let children inherit them. staggerChildren offsets each child's animation in sequence without manual delays.

```
const list = {
  hidden: {},
  show: { transition: { staggerChildren: 0.08 } },
};
const item = {
  hidden: { opacity: 0, y: 12 },
  show: { opacity: 1, y: 0 },
};

<motion.ul variants={list} initial="hidden" animate="show">
  {items.map((i) => (
    <motion.li key={i.id} variants={item}>{i.label}</motion.li>
  ))}
</motion.ul>
```

```
whileInView with viewport once
```

Scroll-reveal pattern. once: true prevents replay on every re-entry; amount controls how much of the element must be visible before firing.

```
<motion.section
  initial={{ opacity: 0, y: 40 }}
  whileInView={{ opacity: 1, y: 0 }}
  viewport={{ once: true, amount: 0.3 }}
  transition={{ duration: 0.6 }}
/>
```

```
Shared-element transition with layoutId
```

Two components with the same layoutId in different trees morph between each other as one unmounts and the other mounts.

```
// In list view
<motion.img layoutId={`hero-${id}`} src={src} />

// In detail view
<motion.img layoutId={`hero-${id}`} src={src} />
```

```
Drag + spring release
```

Gesture handling with built-in spring physics on release. dragConstraints can be a ref or numeric box.

```
<motion.div
  drag
  dragConstraints={{ top: 0, left: 0, right: 200, bottom: 200 }}
  dragTransition={{ bounceStiffness: 600, bounceDamping: 12 }}
/>
```

## Gotchas

- Package was renamed — new code should import from 'motion/react'. The 'framer-motion' import is a legacy alias and may lag behind new APIs.
- AnimatePresence only animates DIRECT children that mount/unmount. A wrapper div between AnimatePresence and the conditional node silently disables exit animations.
- Conditionally rendered children of AnimatePresence need a stable, unique key — without it, React reuses the node and exit never fires.
- layout animations require both the `layout` prop AND a layout-affecting style change (size, flex direction, grid position). Color-only changes won't trigger them.
- whileInView replays on every viewport re-entry by default. Pass viewport={{ once: true }} for marketing-style reveals.
- Animating non-transformable properties (height, width, top, left) is expensive and triggers layout. Prefer transform/scale/translate; use the `layout` prop for genuine size changes.
- In Next.js App Router, motion components are client-only — import from 'motion/react-client' or mark the file 'use client'.
- Motion 11+ requires React 18+. Reduced-motion users still get instant transitions unless you opt out with MotionConfig reducedMotion='never'.

---
Generated by SkillMake from https://motion.dev/docs on 2026-05-09T00:37:56.376Z.
Verify against source before relying on details.

File: ~/.claude/skills/framer-motion/SKILL.md