HomeExperimentsList Transitions

List Transitions

Published Jun 9, 2026
Updated Jun 12, 2026
1 minutes read

Animating a list is where exit animations earn their keep. Add and remove items below — each one enters and leaves on the same spring, and the rest of the list slides to fill the gap instead of snapping.

  • Item 1
  • Item 2
  • Item 3

The pieces

A few rules from the wiki make this work:

  • exit-requires-wrapper — conditionally rendered motion elements need an AnimatePresence wrapper, or the exit animation never runs.
  • exit-key-required — keys must be stable identities, not array indices. Index keys make React reuse the wrong element and the animation lands on the wrong row.
  • exit-matches-initial — the exit state mirrors initial for symmetry; what fades in the same way fades out.
  • mode-pop-layout-for-listsmode="popLayout" pulls exiting items out of layout flow so the survivors can animate into place cleanly.
<AnimatePresence mode="popLayout" initial={false}>
  {items.map((item) => (
    <motion.li
      key={item.id}
      layout
      initial={{ opacity: 0, y: 8, scale: 0.98 }}
      animate={{ opacity: 1, y: 0, scale: 1 }}
      exit={{ opacity: 0, y: 8, scale: 0.98 }}
      transition={{ type: "spring", stiffness: 500, damping: 30 }}
    />
  ))}
</AnimatePresence>

Don't click a ghost

While an item plays its exit animation it's still mounted. presence-disable-interactions says to disable pointer events on exiting elements so you can't click something that's halfway gone. The demo reads useIsPresent() inside each row and sets pointerEvents: "none" once it starts leaving.