---
name: framer-motion-animator
description: "Animation choreography for React. Stagger, scroll-link, layout-shift — all wired correctly. Picks the right Framer Motion primitive for the job (variants vs animate vs layout vs scroll), wires up reduced-motion fallbacks, and outputs typed component code with realistic durations."
license: MIT
---

# Framer Motion Animator

Choreographing motion in React usually fails in one of two ways: either the wrong primitive gets reached for (an `animate` prop where a `layout` animation belongs), or accessibility gets skipped entirely. This skill takes a described interaction — "stagger these cards in on scroll," "animate this list when items reorder" — and produces typed Framer Motion (now `motion`) component code with the correct primitive, sane timing, and a `prefers-reduced-motion` fallback already wired in. It's aimed at React engineers who use Framer Motion but don't want to relearn the variants-vs-layout decision tree every time.

## When to use it

- A card grid or nav menu should reveal with a staggered cascade rather than all at once.
- Elements need to animate as the user scrolls (parallax, progress bars, reveal-on-enter).
- A list reorders, filters, or items mount/unmount and you want smooth position shifts instead of jumps.
- You're getting layout animations that "jump" or distort and aren't sure whether you need `layout`, `layoutId`, or `LayoutGroup`.
- You want motion that respects users who've disabled animation, without hand-writing the media-query branch.

## How it works

It maps each request to the right API:

- **Orchestration** → `variants` with `staggerChildren` / `delayChildren` on a parent, plus `initial` / `animate` / `exit`.
- **Enter/exit of conditional UI** → `AnimatePresence` wrapping the toggled node, with `mode="popLayout"` or `"wait"` where appropriate.
- **Layout shifts** → the `layout` prop, shared-element transitions via `layoutId`, and `LayoutGroup` for coordinated siblings.
- **Scroll-linked** → `useScroll` + `useTransform` (and `useSpring` to smooth), or `whileInView` with `viewport={{ once: true }}` for simple reveals.
- **Reduced motion** → `useReducedMotion()` to swap opacity-only or instant transitions.

Output uses `Transition` types and realistic values (durations around 0.2–0.4s, spring `stiffness`/`damping` rather than arbitrary numbers).

## Example

```tsx
const container: Variants = {
  hidden: {},
  show: { transition: { staggerChildren: 0.08 } },
};
const item: Variants = {
  hidden: { opacity: 0, y: 16 },
  show: { opacity: 1, y: 0, transition: { duration: 0.3 } },
};

export function CardGrid({ cards }: { cards: Card[] }) {
  const reduce = useReducedMotion();
  return (
    <motion.ul variants={container} initial="hidden" whileInView="show"
      viewport={{ once: true, margin: "-80px" }}>
      {cards.map((c) => (
        <motion.li key={c.id} variants={item} layout
          style={reduce ? { opacity: 1 } : undefined}>
          {c.title}
        </motion.li>
      ))}
    </motion.ul>
  );
}
```

## Tips & gotchas

- Children only stagger when they consume `variants` from the parent and you don't override their `initial`/`animate` with literal values — propagation breaks silently otherwise.
- `AnimatePresence` requires a stable, unique `key` on its direct child and only animates `exit` for direct children.
- Combining `layout` with CSS transforms (e.g. `transform: scale`) causes distortion — animate via motion values instead.
- Prefer `transform`/`opacity` over animating `width`, `height`, or `top` for 60fps; use `layout` for size changes.

## FAQ

**Variants or the `animate` prop?** Use `variants` when multiple elements share named states or need orchestration; use `animate` for a single element's one-off transition.

**Does it cover the newer `motion` package?** Yes — it targets the current `motion/react` import path and falls back to `framer-motion` if your version predates the rename.

**Will it touch my styling?** No. It adds motion props and minimal `style`/motion values, leaving your CSS/Tailwind classes intact.
