
Mastering Animate Presence
Audit Motion and Framer Motion usage so modals, conditional UI, and exit animations follow AnimatePresence rules with file:line findings.
Install
npx skills add https://github.com/raphaelsalaja/skill --skill mastering-animate-presenceWhat is this skill?
- 4 prioritized rule categories: Exit Animations, Presence Hooks, Mode Selection, Nested Exits
- Fail/pass examples for exit-requires-wrapper and exit-prop-required patterns
- Outputs findings in file:line format for direct navigation in the editor
- Targets conditional motion elements, modals, and presence-driven UI
- MIT-licensed checker aligned with mastering-animate-presence MDX source v2.0.0
Adoption & trust: 1 installs on skills.sh; 18 GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
Recommended Skills
Journey fit
Ship review is where you catch exit-animation bugs, orphaned motion nodes, and mode mistakes before users see janky unmounts in production. Review subphase matches the skill’s explicit audit workflow: read files, apply rule categories, emit file:line violations.
Common Questions / FAQ
Is Mastering Animate Presence safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Mastering Animate Presence
# Mastering AnimatePresence Review Motion code for AnimatePresence and exit animation best practices. ## How It Works 1. Read the specified files (or prompt user for files/pattern) 2. Check against all rules below 3. Output findings in `file:line` format ## Rule Categories | Priority | Category | Prefix | |----------|----------|--------| | 1 | Exit Animations | `exit-` | | 2 | Presence Hooks | `presence-` | | 3 | Mode Selection | `mode-` | | 4 | Nested Exits | `nested-` | ## Rules ### Exit Animation Rules #### `exit-requires-wrapper` Conditional motion elements must be wrapped in AnimatePresence. **Fail:** ```tsx {isVisible && ( <motion.div exit={{ opacity: 0 }} /> )} ``` **Pass:** ```tsx <AnimatePresence> {isVisible && ( <motion.div exit={{ opacity: 0 }} /> )} </AnimatePresence> ``` #### `exit-prop-required` Elements inside AnimatePresence should have exit prop defined. **Fail:** ```tsx <AnimatePresence> {isOpen && ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} /> )} </AnimatePresence> ``` **Pass:** ```tsx <AnimatePresence> {isOpen && ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} /> )} </AnimatePresence> ``` #### `exit-key-required` Dynamic lists inside AnimatePresence must have unique keys. **Fail:** ```tsx <AnimatePresence> {items.map((item, index) => ( <motion.div key={index} exit={{ opacity: 0 }} /> ))} </AnimatePresence> ``` **Pass:** ```tsx <AnimatePresence> {items.map((item) => ( <motion.div key={item.id} exit={{ opacity: 0 }} /> ))} </AnimatePresence> ``` #### `exit-matches-initial` Exit animation should mirror initial for symmetry. **Fail:** ```tsx <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ scale: 0 }} /> ``` **Pass:** ```tsx <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: 20 }} /> ``` ### Presence Hook Rules #### `presence-hook-in-child` useIsPresent must be called from child of AnimatePresence, not parent. **Fail:** ```tsx function Parent() { const isPresent = useIsPresent(); // Wrong location return ( <AnimatePresence> {show && <Child />} </AnimatePresence> ); } ``` **Pass:** ```tsx function Child() { const isPresent = useIsPresent(); // Correct location return <motion.div data-exiting={!isPresent} />; } ``` #### `presence-safe-to-remove` When using usePresence, always call safeToRemove after async work. **Fail:** ```tsx function AsyncComponent() { const [isPresent, safeToRemove] = usePresence(); useEffect(() => { if (!isPresent) { cleanup(); // Never calls safeToRemove } }, [isPresent]); } ``` **Pass:** ```tsx function AsyncComponent() { const [isPresent, safeToRemove] = usePresence(); useEffect(() => { if (!isPresent) { cleanup().then(safeToRemove); } }, [isPresent, safeToRemove]); } ``` #### `presence-disable-interactions` Disable interactions on exiting elements using isPresent. **Fail:** ```tsx function Card() { const isPresent = useIsPresent(); return <button onClick={handleClick}>Click</button>; // Button clickable during exit } ``` **Pass:** ```tsx function Card() { const isPresent = useIsPresent(); return ( <button onClick={handleClick} disabled={!isPresent}> Click </button> ); } ``` ### Mode Selection Rules #### `mode-wait-doubles-duration` Mode "wait" nearly doubles animation duration; adjust timing accordingly. **Fail:** ```tsx <AnimatePresence mode="wait"> <motion.div transition={{ duration: 0.3 }} /> </AnimatePresence> // Total