FLIP Animation
FLIP: record First positions, shuffle items to get Last positions, Invert by applying a CSS transform that puts each element back in its First position, then Play by removing the transform. The browser animates from the inverted start to zero transform. Items appear to glide, not teleport.
When you shuffle the list, each item appears to glide to its new position. No absolute positioning, no left/top animation, no layout engine. The DOM reorders instantly; the animation is a visual lie. This is FLIP.
First. Before mutating state, record the bounding box of every element: el.getBoundingClientRect(). Store keyed to item identity, not index.
Last. Mutate state — React re-renders, the DOM reflects the new order. Items have moved to their final positions.
Invert. Now compute delta = first.top - last.top for each element. Apply a CSS transform that puts the element visually back in its First position: transform: translateY(delta). Apply it with transition: none so it snaps immediately without animating.
Play. Remove the transform in the next requestAnimationFrame, restoring the element to its Last position. Add the transition: transform 380ms spring. The browser animates from the inverted-First to the real-Last (zero transform). The user sees motion; the layout never moved.
Why key matters. FLIP requires stable element identity across renders. React's key prop ensures the same DOM node is reused for the same list item. Without stable keys, React unmounts the old node and mounts a new one — no node to apply the inversion to.
The double requestAnimationFrame. Applying the transform and then removing it in the same frame is a race condition — the browser may batch the style changes and never render the inverted state. Two nested rAFs guarantee the inversion is painted before the transition starts.