Layout animation (FLIP technique)

Making elements glide to their new positions when the layout changes, instead of teleporting there on the next frame.

it glides when the list reordersthings jump instead of movingflip animation techniqueanimate between layout positionsfirst last invert playsmooth reorder animationanimate width and height without jankthe cards slide into their new spots

See it

Live demo coming soon

What it is

When a list filters, a card expands, or a grid reflows, the browser moves elements instantly on the next frame. Layout animation covers that gap: the element slides from where it was to where it now is. The standard recipe is FLIP, named by Paul Lewis: First (measure the old box), Last (apply the real layout change and measure the new box), Invert (apply a transform that visually snaps it back to the old box), Play (animate that transform to zero).

The point of the inversion is performance. Animating 'top', 'left', 'width', or 'height' makes the browser re-layout every frame; animating 'transform' and 'opacity' rides the compositor. In practice you rarely hand-roll it: Framer Motion's 'layout' prop, react-flip-toolkit, and the View Transitions API all do FLIP or a snapshot equivalent for you.

Gotcha: scaling a container with 'transform: scale' scales its children too, so text and border radii visibly squash mid-flight. Fix it by counter-scaling children or by animating width and height directly for the few elements that need it. Second trap: the measure step calls getBoundingClientRect, which forces synchronous layout, so batch every read before any write or you thrash the frame budget.

Ask AI for it

Animate this list's layout changes using the FLIP technique: before the reorder or filter, record each item's bounding box; after the DOM updates, record the new box, apply an inverting translate and scale so each item appears unmoved, then animate that transform to zero over 300ms with an ease-out curve. Only animate transform and opacity, counter-scale child text so it does not squash, stagger items by 20ms, and batch all measurement reads before any style writes. If Framer Motion is available, use the 'layout' prop with a layoutId per item instead.

You might have meant

shared element transitionview transitions apimorphintrinsic size animationstagger

Go deeper