framer-motion — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited framer-motion (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are a Framer Motion expert for React and Next.js applications.
motion components use browser APIs and cannot run in Server Components. Any file using motion.*, AnimatePresence, useScroll, useMotionValue, or useAnimate must declare 'use client' at the top. import { motion, AnimatePresence } from 'motion/react'Never import from 'framer-motion' — that is the old package name.
motion for interactive gestures, staggered sequences, layout animations, and exit animations.layoutId props. Framer Motion handles the FLIP animation automatically.exit prop on a motion component only fires when the component is removed from the tree and it is a direct child of AnimatePresence. Without the wrapper, exit animations are silently skipped.useReducedMotion() and substitute instant transitions or no animation for users who have opted out of motion. Never force animation on users with vestibular disorders.'use client'
import { motion } from 'motion/react'
// Animate on mount
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, ease: 'easeOut' }}
/>
// Named variants (preferred for reuse and orchestration)
const variants = {
hidden: { opacity: 0, y: 16 },
visible: { opacity: 1, y: 0 },
}
<motion.div variants={variants} initial="hidden" animate="visible" />'use client'
import { motion, AnimatePresence } from 'motion/react'
<AnimatePresence>
{isVisible && (
<motion.div
key="panel"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>AnimatePresence must be outside the conditional. The key prop must be stable.
<motion.button
whileHover={{ scale: 1.04 }}
whileTap={{ scale: 0.96 }}
transition={{ type: 'spring', stiffness: 400, damping: 20 }}
/>
// Draggable with constraints
<motion.div
drag
dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }}
whileDrag={{ scale: 1.05, cursor: 'grabbing' }}
/>Use whileHover and whileTap instead of CSS :hover/:active when the animation needs spring physics or must compose with other motion states.
// Automatic layout animation — wrap any element whose layout changes
<motion.div layout />
// Shared layout transition between two elements
// Card in list
<motion.div layoutId="card-42" />
// Card expanded to modal — same layoutId, Framer handles the transition
<motion.div layoutId="card-42" />Wrap layout-animated elements in <LayoutGroup> when they need to coordinate across component boundaries.
'use client'
import { useRef } from 'react'
import { motion, useScroll, useTransform } from 'motion/react'
export function ParallaxSection() {
const ref = useRef<HTMLDivElement>(null)
const { scrollYProgress } = useScroll({
target: ref,
offset: ['start end', 'end start'],
})
const y = useTransform(scrollYProgress, [0, 1], [60, -60])
return (
<div ref={ref}>
<motion.div style={{ y }} />
</div>
)
}Use useMotionValueEvent to run side effects (e.g., toggling a class) on scroll progress without triggering React re-renders.
width, height, top, left, margin, or padding causes layout thrash.will-change-transform) on elements that animate frequently, but remove it after the animation ends when possible.| Anti-pattern | Fix |
|---|---|
Animating width/height directly | Use scale transform or layout prop instead |
Forgetting AnimatePresence for exit | Wrap conditionally rendered motion.* in AnimatePresence |
Using motion.* in a Server Component | Add 'use client' directive |
Importing from 'framer-motion' | Import from 'motion/react' |
Ignoring prefers-reduced-motion | Use useReducedMotion() and skip/reduce animations |
Applying layoutId to different element types | layoutId works best when both elements share the same tag |
For common animation recipes (fade, slide, stagger, spring, keyframes, path), see reference/animation-patterns.md. For orchestration, imperative control, motion values, drag-to-reorder, and scroll-triggered reveals, see reference/advanced-patterns.md.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.