Try to animate an element from one grid position to another and you hit the wall: layout properties—top, left, width, grid position—trigger reflow on every frame. Sixty reflows a second is a slideshow. Meanwhile transform animates on the compositor, silky at any complexity. The problem: transforms cannot know where the element is going.

FLIP inverts the problem: First, Last, Invert, Play. Let the layout change happen instantly, then use transforms to make it look gradual.

The four steps

// First: measure where it is
const first = el.getBoundingClientRect();

// Last: apply the change, measure again
container.appendChild(el);
const last = el.getBoundingClientRect();

// Invert: transform it back to where it was
const dx = first.left - last.left;
const dy = first.top - last.top;

// Play: release to identity — compositor does the rest
el.animate([
  { transform: `translate(${dx}px, ${dy}px)` },
  { transform: 'none' }
], { duration: 350, easing: 'cubic-bezier(.2,.8,.2,1)' });

“The element already moved. The animation is a flashback.”

Why this is everywhere

Shuffle a filtered product grid, expand a card into a modal, reorder a kanban column—every buttery version of those you have seen is FLIP. Framer Motion’s layout prop, Vue’s TransitionGroup, the View Transitions API itself—all FLIP at heart: snapshot, change, invert, play.

Two practical notes. Batch your measurements—read all Firsts, mutate, read all Lasts—or interleaved reads and writes will thrash layout. And animate scale rather than width/height for size changes, correcting child distortion with inverse scales. Master the pattern once and the entire category of “impossible” layout animations becomes an afternoon task.