AnimatePresence Exit Animation Skipped When Child Keys Are Not Stable
0 reputation · 12 Jan 2020, 22:35 UTC
0 reputation · 12 Jan 2020, 22:35 UTC
AnimatePresence is the Framer Motion component that keeps a child mounted long enough for its exit variant to play before React removes it from the tree. Its behavior depends on matching each exiting child to the element that previously occupied that position.
When keys are missing, reused, or derived from array indices, that matching becomes ambiguous. The exit variant may not run, may run on the wrong element, or may appear skipped when several children change in one render. The mode prop adds another variable: the default sync lets exiting and entering children coexist, while wait serializes them and changes when layout space is released.
The goal is to determine which combination of key strategy and mode reliably produces a visible exit transition for a conditionally rendered list. Version assumption: the exit, mode, and key-tracking semantics referenced here correspond to the current Framer Motion API; confirm against the installed version.
mode interact with key changes that occur within a single render pass?exit prop?33775 reputation · 13 Jan 2020, 06:50 UTC
1. Key requirement: AnimatePresence matches exiting elements to previously rendered children by key. A stable, unique key is required on **every child that can exit** – not only on those that appear conditionally. If a child that might disappear is rendered without a stable key (or with a reused/array‑index key), React may reuse the same DOM node across renders, and Framer Motion cannot detect an exit, so the exit variant is never applied.
2. mode interaction with key changes: mode controls how exiting and entering children coexist. In sync mode, all exiting and entering children are rendered simultaneously; in wait mode, exiting children finish before new ones are mounted. Key changes occur *before* mode logic is applied: a new key is treated as a new element, and the previous element (with its exit variant) is scheduled to leave. Thus the mode does not alter the key‑matching logic; it only determines when the exit animation completes relative to the entrance.
3. Minimal reproduction to isolate key‑tracking failure:
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";
const Item = ({ id, text }) => (
{text}
);
export default function Demo() {
const [items, setItems] = useState([
{ id: "a", text: "First" },
{ id: "b", text: "Second" },
]);
const toggle = () => {
setItems(prev => (prev.length === 2 ? prev.slice(1) : [...prev, { id: "c", text: "Third" }]));
};
return (
Toggle
{items.map(item => (
))}
);
}
If you replace key={item.id} with key={index} or omit the key entirely, the exit animation will be skipped or applied to the wrong element. The same minimal snippet with stable keys demonstrates the exit animation correctly.
npm list framer-motion or yarn list framer-motion and confirm the major version.exit props – each child component should expose an exit variant. If omitted, the component will mount/unmount instantly even if AnimatePresence is present.mode settings – try mode="sync" and mode="wait" in a controlled environment. The exit animation should still run; wait simply delays the entrance until after exit completes.exit props.After applying the above steps, if the exit animation is still not visible:
exit variant is correctly defined and not overridden by a parent animate prop.display: none instead of visibility: hidden).key warnings appear for unrelated lists, as they can indicate a broader key management issue.To tailor the recommendation further, could you share the Framer Motion major version you’re using? This will confirm that the described behavior applies to your environment.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.