- 1Item 1
- 2Item 2
- 3Item 3
- 4Item 4
The shape of the problem
AnimatePresence exists to solve one specific thing: animating an element out before React removes it from the tree. Left alone, React rips a component out the instant it leaves the array. AnimatePresence holds it around just long enough to play its exit animation first.
The interesting question is what happens during the overlap — the moment one element is still leaving and the layout around it wants to move. That's what the mode prop decides, and the default isn't usually the one you want.
The three modes
sync— the default. Exiting and entering elements animate at the same time, and crucially the exiting element keeps its space in the layout until its animation finishes. Remove an item from the middle and the gap stays open while it fades, then everything snaps up once it's gone. Fine for a single element fading in place. On a list, it's the source of that slightly jerky wait, then jump.wait— finish the exit completely before starting the enter. This is built for swapping one thing for another: a tab panel, a step in a flow, the active item in a single-slot view. The outgoing content leaves, and only then does the incoming content arrive. Point it at a list of many items and it serialises everything, which is rarely what you're after.popLayout— the one I reach for on lists. The exiting element is popped out of the layout flow — set toposition: absolute— the instant it starts leaving, so the rows below don't wait for it. They reflow into the gap immediately while the old element animates out over the top of them. This is what makes a removal feel like the list closing around the missing item rather than lurching after it.
What to look for in the demo
Set the mode to sync and remove the second item. The gap it leaves sits open for the length of the exit, and the rows below only move once it's fully gone. Now switch to popLayout and do the same thing. The rows underneath start sliding up the moment you click, while the removed row fades away above them. Same exit animation, same spring on the layout reflow — the only thing that changed is who waits for whom.
wait is the odd one out here on purpose. It isn't wrong, it's answering a different question — the single-slot swap, not the multi-item list — and watching it behave a little awkwardly on a list is the quickest way to remember what it's actually for.
The rule I've settled on
Reach for popLayout whenever items reflow: lists, grids, anything where removing one thing should move the others. Reach for wait when exactly one thing occupies a slot and you're replacing it. Leave it on sync only when nothing around the animating element needs to move. The default is the default because it's the simplest, not because it's the one you'll want most often.