Every time you see a list reorder itself smoothly, a card morph into a modal, or a tab indicator slide between tabs, the underlying mechanism is the same four-step dance called FLIP. It's one of the most important techniques in web animation, and it's often invisible because when it works, it just looks like magic. Understanding FLIP lets you implement shared-element transitions that would be impossible with naive CSS.
#The idea
FLIP stands for First, Last, Invert, Play. The technique was named by Paul Lewis in 2015 but has been used in graphics programming for decades. It lets you animate between two layout states, not just two positions, without knowing the destination ahead of time.
The steps:
- First: measure the element's position before the layout change (
getBoundingClientRect). - Last: perform the layout change and measure the element's new position.
- Invert: apply a transform that makes the element look like it's still in its first position (a negative translate).
- Play: animate the transform back to identity. The element appears to smoothly move from its First to its Last position, even though the layout change happened instantaneously.
FLIP is powerful because it animates layout results rather than specific positions. Drop an element into a grid, and the grid will reflow. FLIP makes that reflow look smooth. Sort a list, and each item slides to its new position.
Motion provides this via the layout prop and layoutId prop. You tag elements, and Motion handles the FLIP machinery automatically.
#When it applies
- Lists reordering: dragging items, sorting, filtering.
- Shared-element transitions: clicking a thumbnail that expands into a full view; the image appears to move from one place to another rather than fade and appear.
- Tab indicators: the sliding underline between tabs.
- Grid rearrangement: filtering a gallery; remaining items slide to fill gaps.
- Modal-from-button: opening a dialog that visually emerges from the trigger button.
#When it doesn't
- Simple enter/exit: fading a modal in has no FLIP component; you know both positions upfront.
- Static layouts: if nothing rearranges, FLIP is overkill.
- Text-heavy reflow: reflowing paragraphs of text with FLIP is expensive and often jarring; the text should just reflow instantly.
#Implementation
Auto-animating layout changes with layout:
// Motion measures the element before/after any layout change
// and animates the difference for free.
<motion.div layout transition={{ duration: 0.3 }}>
{children}
</motion.div>Shared-element transition with layoutId:
// Same layoutId across two components = FLIP between them
// when one unmounts and the other mounts.
{isOpen ? (
<motion.div layoutId="card" className="fullscreen-modal">
{content}
</motion.div>
) : (
<motion.div layoutId="card" className="thumbnail">
{content}
</motion.div>
)}- Both elements with the same
layoutIdshare an identity. Motion measures the first, unmounts it, mounts the second, and animates from first to second. - This is exactly how the sidebar's rail works in Moro.
#Craft notes
layoutIdis global. Two elements anywhere in the tree with the samelayoutIdwill animate between each other. Namespace carefully.layoutanimates all layout changes, including size, position, and aspect ratio. If you only want position, combine with specific properties.- Text distorts during layout animations unless you wrap it. The workaround is to animate the container layout and keep the text size stable via
layout='position'. - FLIP can be expensive on large element counts. Reordering 100 items with
layoutwill measure each. UseAnimatePresencewith manual exit animations for very large lists. - Nanda's essay is the canonical teaching of FLIP. If this lesson is the 101, Nanda's is the 200-level. Worth reading after this one.
#Exercises
- Build a tab indicator. Create three tabs. Add a sliding underline using
layoutId. Without FLIP, you'd need to compute positions manually. WithlayoutId, Motion does it. - Animate a filter. Take a grid of items with a filter. Apply
layoutto each item. As the grid filters down, remaining items should slide to their new positions. - Shared element between routes. Try to build a thumbnail-to-detail transition where the thumbnail image morphs into the hero of the detail view. This is the hardest version of FLIP to do well.
#Study this in the wild
- Moro's own sidebar. Sliding rail on active lesson uses
layoutId. Open DevTools and watch the transforms during click. - Linear's issue panel. Opening an issue from the list uses shared-element transitions. The issue title's position appears stable as the panel expands around it.
- iOS photo app. Tapping a thumbnail expands it to full screen via shared-element. Native implementation, same principle.
#Further reading
- Nanda: Inside Framer Motion's Layout Animations — the definitive pedagogical essay on FLIP.
- Paul Lewis: FLIP your animations — the 2015 original that named the technique.
- Motion: Layout animations.