Implementing Smooth Entry & Exit Animations in React with Framer Motion’s AnimatePresence
Learn how to add entry and exit animations to React components with Framer Motion’s AnimatePresence. This guide covers prerequisites, key steps, runtime checks, common pitfalls, and recovery options for smooth, reliable UI transitions.
31 Jul 2026, 03:26 UTC

Goal: Seamless Mount & Unmount Animations
When a React component appears or disappears, developers often want a visual cue that the UI is changing. Framer Motion’s AnimatePresence component lets you animate components on unmount, turning abrupt jumps into fluid transitions. This guide walks through the exact setup, common pitfalls, runtime checks, and recovery strategies so you can reliably add entry/exit animations to any React list or conditional UI.
Prerequisites
- React 18+ (hooks support required)
- Framer Motion v10+ installed via
npm i framer-motion - Basic understanding of React functional components and JSX
- Knowledge of CSS or inline style objects for animation values
Step‑by‑Step Procedure
- Wrap the parent container with
AnimatePresencePlace
AnimatePresencearound the part of the tree that will receive dynamic children. It must be a sibling of themotionelements that declare anexitprop.import { AnimatePresence, motion } from "framer-motion"; const ItemList = ({ items }) => ( <AnimatePresence> {items.map(item => ( <motion.div key={item.id} layout initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.3 }} > {item.text} </motion.div> ))} </AnimatePresence> ); - Define a stable
keyReact uses the
keyto track element identity across renders. If the key changes or is omitted, Framer Motion cannot pair a mount with its eventual unmount, causing the exit animation to be skipped. Use a unique, stable identifier (e.g.,item.id). - Set the
exitprop on eachmotionchildThe
exitprop tells Framer Motion what animation to run when the component is removed from the React tree. It can be a style object, a variant name, or a function that receives the component’s current state. - Ensure the component is actually unmounted
Conditional rendering must remove the element from the DOM. If you hide it via CSS or a parent
display:none,AnimatePresencewill not trigger the exit animation. Use logic like{condition && …}or manipulate the array that feedsmap. - Optional: Enable shared layout transitions
For moving items (e.g., reordering a list), add
layoutIdto eachmotionelement and wrap the parent withAnimateSharedLayout(orLayoutGroupin newer versions). This creates smooth positional transitions between mount and unmount.
Runtime Checks & Diagnostics
- Console warnings – Framer Motion logs a message if an
exitprop is missing on amotionchild insideAnimatePresence. Look for “Missing exit prop” warnings and add the prop immediately. - Animation timeline – Open the browser’s developer tools, switch to the Animations panel, and watch the timeline. The exit animation should appear after the component is removed.
- React DevTools – Inspect the component tree after triggering a removal. The motion element should disappear only after the animation’s
durationhas elapsed.
Common Pitfalls & Recovery Strategies
- Missing
exitpropResult: component vanishes instantly. Fix: add an
exitstyle or variant. - Improper parent element
Result: exit animation may not play because the parent isn’t a
motionelement. Fix: wrap the parent withmotion.divor useLayoutGroup. - Unstable keys
Result: Framer Motion cannot match mount/unmount pairs, skipping exit. Fix: ensure keys are stable across renders.
- Excessive nesting of
AnimatePresenceResult: performance overhead and potential animation glitches. Fix: keep a single
AnimatePresencewrapper per dynamic list or conditional block.
Recovery Options
- Fallback to CSS transitions – If Framer Motion fails to animate, you can add a CSS class with
transitionproperties as a safety net. - Graceful degradation – Wrap the animation logic in a try/catch block or conditionally render the animated component only when the environment supports
requestAnimationFrame. - Performance monitoring – Use
requestAnimationFrameandperformance.now()to log frame drops during the animation and adjustdurationoreasingaccordingly.
Practical Example: A Deleting Todo List
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
const TodoItem = ({ id, text, onDelete }) => (
<motion.div
key={id}
layout
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8, y: -20 }}
transition={{ duration: 0.25 }}
className="todo-item"
>
{text}
<button onClick={() => onDelete(id)}>X</button>
</motion.div>
);
const TodoList = () => {
const [todos, setTodos] = useState([
{ id: 1, text: "Buy milk" },
{ id: 2, text: "Walk dog" },
]);
const deleteTodo = id => setTodos(t => t.filter(t => t.id !== id));
return (
<div className="list-container">
<AnimatePresence>
{todos.map(t => (
<TodoItem key={t.id} {...t} onDelete={deleteTodo} />
))}
</AnimatePresence>
</div>
);
};
Run this component, click the X button, and observe the exit animation. If the animation doesn’t play, check the console for missing exit warnings or unstable keys.
Conclusion
By wrapping dynamic content with AnimatePresence, supplying stable keys, and defining explicit exit animations, you can turn abrupt unmounts into polished UI transitions. Always verify with the browser’s animation timeline and React DevTools, and use the recovery strategies above if the animation fails or performance drops.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.