| Need | Use | Not |
|---|---|---|
| Declarative animation tied to component state | animate prop / variants | imperative animate() for simple cases |
| One-off imperative sequence (timeline-like) | useAnimate | chaining many animate prop changes |
| Value that updates every frame without re-render | useMotionValue / useTransform | useState + re-render |
| Animate on scroll position | useScroll (+ useTransform) | manual scroll listeners |
| Physics-feel motion (drag release, follow-cursor) | useSpring / spring transition | tween with a guessed duration |
| Predictable, fixed-length animation | tween transition | spring (spring duration isn't fixed) |
| Exit-before-unmount animation | AnimatePresence + exit | conditional render without wrapper |
| Position/size change on layout shift | layout prop | manually animating top/left/width |
| Shared-element transition across components | layoutId | separate independent layout animations |
| Sequenced children animation | staggerChildren on parent variant | per-child manual delay values |
whileHover/whileTap/whileDrag.transform and opacity first; treat any other animated property as a potential jank source.layout prop to the smallest subtree that actually changes, not a whole page wrapper.requestAnimationFrame runs every ~16ms in every browser/tab state, Safari/Firefox can throttle it.reducedMotion once via MotionConfig at the app root instead of per-component checks."user" respects the OS setting; "always" forces reduced motion regardless of OS setting, use sparingly.ease array has the wrong length.dragConstraints ref points at an element that isn't mounted/hydrated yet.React and Vue APIs mirror each other closely (useSpring/useSpring, motion.div/motion.div, etc.); when in doubt, the React doc chapter's concepts transfer directly, only the component syntax differs. Vanilla JS uses the lower-level animate(), scroll(), and motion-value functions directly, without hooks.