Framer Motion's layout Prop: What FLIP Gets You and What It Costs
Framer Motion's layout prop animates DOM reflows using FLIP and transform-only compositing. Here's how it works, a working reorder example, and the performance and SSR trade-offs to check before shipping.
31 Jul 2025, 21:49 UTC

You reorder a list in React and the items teleport. No transition, no sense of continuity — just a DOM snapshot before and after. The usual fix involves measuring positions by hand, storing refs, and writing transform math nobody wants to maintain. Framer Motion's layout prop collapses all of that into one attribute, and understanding how it works tells you exactly when to reach for it and when to leave it off.
The trick: measure, invert, animate
The layout prop is built on the FLIP technique — First, Last, Invert, Play. When a layout change happens (a reorder, a flex reflow, a conditional render), Framer Motion measures the element's bounding box before and after the change. It then applies a transform that makes the element appear to still be in its old position, and animates that transform back to identity. The element never actually animates top, left, width, or height — only transforms, which the browser can run on the compositor thread.
That last point is the whole value proposition. Transform-only animation is what keeps these transitions near 60fps even when the underlying layout change is expensive.
A concrete example: reorderable list
Assume React 18 and Framer Motion 11 or later. In a client-rendered component:
import { motion } from "framer-motion";
import { useState } from "react";
export function ReorderableList() {
const [items, setItems] = useState(["alpha", "beta", "gamma"]);
const shuffle = () => setItems([...items].reverse());
return (
<>
Shuffle
{items.map((id) => (
{id}
))}
);
}The key prop does the heavy lifting for identity; layout does the rest. To verify it's actually cheap, open DevTools → Performance, record a shuffle, and confirm you see transform compositing without layout/paint spikes on every frame. Don't trust how it looks — trust the trace.
A sibling feature worth knowing: layoutId. Give two elements in different components the same layoutId (unique within its boundary), and Framer Motion morphs one into the other across mount/unmount — the classic thumbnail-to-fullscreen transition. Each layoutId must be unique; duplicates produce undefined behavior.
Where the cost shows up
Every layout component registers a projection node, and each layout change triggers measurement passes across the tree. With a handful of nodes this is invisible. With 50–100+ nodes — say, a dense dashboard grid — the measurement work lands on the main thread and can blow a frame budget, especially on mobile. Two mitigations:
- Add
layoutRootto a parent to isolate measurement to that subtree instead of letting it propagate up the document. - Profile before optimizing: record a reorder with 100 items and check whether frame time stays under ~16ms on your target hardware. If it does, stop worrying.
Other sharp edges worth knowing before production:
- Don't mix with CSS transitions. A CSS
transitionon transform or position properties on the same element fights Framer Motion's inline transform overrides and produces jank or stuck states. - Wrap conditional layout elements in
AnimatePresence. Unmountedlayoutcomponents without it can leave orphaned projection nodes — a slow memory leak in long-lived views. - SSR flash. Layout animations need client-side measurement, so a server-rendered
layoutelement can jump on first paint. The standard fix is to start withlayout={false}and enable it in auseEffectafter mount. - Reduced motion is handled for you. Layout animations disable automatically under
prefers-reduced-motion; other animation types respect thereducedMotionsetting separately. Verify with your OS reduce-motion toggle rather than assuming.
Debugging hooks you probably haven't used
The layout prop family includes callbacks that make coordination easier: onLayoutMeasure receives the projected box ({ x, y, width, height }), and onLayoutAnimationStart/onLayoutAnimationComplete let you sequence side effects — restoring scroll position after a container animates is the canonical use. These are the right place for logic that would otherwise require guessing animation durations.
The decision rule
Use layout when the DOM change is structural and the continuity genuinely helps comprehension — reordering, expanding panels, shared-element transitions. Skip it for decorative motion (use animate/whileHover instead), for very large lists without layoutRoot, and anywhere you're already animating the same properties with CSS. Then verify with a performance trace on the slowest device you support. The prop is one word; the discipline is knowing what it costs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.