What happens to Framer Motion exit animations when a component with the same layoutId remounts before the exit completes?
0 reputation · 06 Sept 2023, 12:15 UTC
0 reputation · 06 Sept 2023, 12:15 UTC
When a list item that uses a layoutId is removed from the DOM via animatePresence and then re‑inserted with the same layoutId after a short delay, we want the exit animation to finish cleanly without being interrupted by a new enter animation, so the UI shows a smooth fade‑out followed by a fade‑in.
The exact timing of the exit animation relative to the remount is not documented; Framer Motion may interrupt the exit and start a new enter animation if the same layoutId appears before the exit finishes, potentially causing a visual glitch. Additionally, stale layout measurements may persist after unmount, and behavior may differ between Framer Motion versions 10.x and 11.x or under React StrictMode.
What determines whether the exit animation is cancelled when a new component with the same layoutId mounts? Which props or presence values can be used to synchronize the exit and enter animations to avoid interruption? How does React StrictMode influence the observed timing?
31775 reputation · 06 Sept 2023, 16:15 UTC
Remounting a component with the same layoutId does not cancel the exit animation. AnimatePresence tracks exiting children by React key, not layoutId, so the exiting copy keeps running until its exit transition finishes and is then removed. What you see is usually a shared layout handoff: the new element mounts with a matching layoutId and animates from the old bounding box, making the old element's exit appear cut short.
Confirmed, well‑established behavior:
layoutId remount is a separate presence entry.layoutId triggers a shared layout transition from the last known bounding box of the previous element with that id.onExitComplete fires only after all exiting children finish.Plausible but version‑sensitive: the exact timing of the layout snapshot handoff and whether stale measurements persist after unmount. This changed across major releases (notably around the layout‑animation refactors between 10.x and 11.x), so treat any claim that the exit is "cancelled" as an interpretation to verify against your installed version, not a guarantee.
mode=\"wait\" on AnimatePresence. This forces the exiting child to fully complete before the new child enters. mode=\"sync\" (default) runs both simultaneously; mode=\"popLayout\" pops the exiting element out of layout flow — neither guarantees sequencing.onExitComplete. Defer setting the state that re‑adds the item until this callback fires. This guarantees zero overlap at the cost of a forced delay equal to the exit duration.layoutId on the remount if you don't need the shared transition. Removing the shared id eliminates the handoff artifact entirely.Note: there is no public presence prop for cross‑component coordination — PresenceChild/presence context are internal. User‑facing synchronization is mode, onExitComplete, and per‑component exit transition duration.
In development, StrictMode double‑invokes mount/unmount effects, so presence registration and layout measurements run twice. This can produce flicker or apparent "instant remounts" that never happen in production. Verify any timing anomaly in a production build with StrictMode off before tuning durations.
Build a minimal repro with your exact installed version: remove the item and re‑insert it after a delay shorter than the exit duration. Log a timestamp in onExitComplete and compare it to the remount time — this tells you whether the exit truly was interrupted or whether you are only seeing the layout handoff. Repeat with mode=\"wait\" to confirm the enter starts only after exit completion.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.