Diagnosing Framer Motion AnimatePresence Exit Animations That Skip Before Unmount
When a Framer Motion component vanishes instantly instead of playing its exit animation, the cause is usually a missing AnimatePresence wrapper, absent exit prop, or unstable React key. This guide walks through ordered checks and fixes.
01 Nov 2025, 16:34 UTC

A conditionally rendered Framer Motion component that disappears instantly on removal—without playing its configured exit animation—is usually a structural problem, not a bug in the animation library. The useful takeaway: React removes the DOM node before Framer Motion can intervene unless the element sits inside an AnimatePresence wrapper, declares an exit target, and carries a stable key. This guide isolates which of those three conditions is missing.
Recognizable condition
You toggle a boolean state, a motion component is removed from the tree, and the element vanishes in a single frame. No opacity fade, no slide, no scale-down. If the same component animates correctly on mount or update, the exit path is the only broken part. The exit animation is not merely ignored; it never starts because the DOM node is gone before Framer Motion can schedule it.
Cause and diagnostic table
| Likely cause | Diagnostic signal | Fix summary |
|---|---|---|
Conditional render not wrapped in AnimatePresence | Removing the element unmounts it immediately; no exit timing exists | Wrap the conditional block in a single AnimatePresence |
motion child lacks exit prop or variants.exit | AnimatePresence is present but removal is still instant | Add exit={{ opacity: 0 }} or a variants.exit object |
| Missing or unstable React key | Element is treated as new rather than exiting; siblings may re-mount instead of exit | Assign a stable, unique key to each motion child |
mode or major-version mismatch | Exited nodes linger too long or layout shifts appear during exit | Verify installed version and set mode intentionally |
| StrictMode double-mounting in development | Exits appear inconsistent only in dev, but behave normally in a production build | Verify behavior in a production build before treating as a bug |
Ordered checks and fixes
Check 1 — Is the conditional render inside AnimatePresence?
AnimatePresence is a React component that delays unmounting of its direct children until exit animations finish. If your conditional render sits outside it, React unmounts the node synchronously and Framer Motion never gets a chance to run the exit sequence.
Fix: Wrap the conditional block—not each child individually—in a single AnimatePresence. Use one wrapper for sibling elements that need coordinated exit timing.
import { AnimatePresence, motion } from "framer-motion";
function Panel({ isOpen }) {
return (
<AnimatePresence mode="wait">
{isOpen && (
<motion.div
key="panel"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Panel content
</motion.div>
)}
</AnimatePresence>
);
}
Check 2 — Does the motion child declare an exit target?
AnimatePresence only knows how long to wait if the motion child exposes an exit animation. Without an exit prop or a variants.exit entry, the library has no duration to observe and removes the node immediately.
Fix: Add exit={{ opacity: 0 }} for a simple fade, or reference variants.exit if you use variant labels. Note that animate runs on mount and update, not unmount; exit is the only prop that controls removal.
Check 3 — Are React keys stable and unique?
React uses keys to decide whether an element is the same instance across renders. If the key changes—or is missing in a list—React treats the element as new and mounts a fresh node instead of running the exit path on the old one.
Fix: Give each conditional motion child a stable string key that does not depend on array index or transient state.
Check 4 — Does the mode match your layout expectations?
AnimatePresence mode defaults and behavior differ across major Framer Motion versions. mode="wait" delays unmounting the new component until the exiting one finishes, which is useful for single-element swaps but keeps exited nodes mounted longer. mode="popLayout" targets layout transitions and removes exiting elements from layout flow immediately. The default sync mode runs enter and exit concurrently.
Fix: Read the installed version from package.json and compare its AnimatePresence behavior against the release notes for that major version. Set mode explicitly rather than relying on defaults.
Practical verification
Before concluding the exit path is broken, run these isolation tests:
- Toggle the conditional
motioncomponent off insideAnimatePresence mode="wait"and inspect the DOM. The node should remain until the exit animation completes. - Temporarily remove the
AnimatePresencewrapper and confirm the exit no longer plays, isolating the wrapper as the deciding factor. - Read the installed Framer Motion version from
package.jsonand compare itsAnimatePresencebehavior against the release notes for that major version.
Escalation criteria
If the checks above pass but exits still skip, escalate by producing a minimal reproduction that includes the exact Framer Motion and React versions. Search for known StrictMode or concurrent-rendering issues; React StrictMode can double-mount components in development, making exits look inconsistent. Verify behavior in a production build before filing a bug.
Limitations
One caution: avoid mode="wait" on long lists unless necessary, because it keeps exited nodes mounted until animations finish, increasing DOM cost. Also, do not wrap each motion child in its own AnimatePresence; use one wrapper for sibling elements that should coordinate exit timing.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.