Framer Motion layout Prop: Automatic FLIP vs Manual Keyframes Trade-offs
Automatic layout animations with Framer Motion's layout prop save keyframe work but require measurement passes. Here's when to use them and when manual keyframes are safer.
06 Apr 2026, 03:19 UTC

Reordering a list in React should feel effortless. Add a state change, the DOM moves, and the user sees a jump. Hand-coding keyframes for every reorder fixes the jump but creates maintenance debt. Framer Motion's layout prop offers a middle path: automatic FLIP-based animation for layout changes without explicit keyframes.
The thesis is simple. Use layout when the animation is a direct consequence of layout, not a designed motion. It saves code and stays correct as the UI changes, at the cost of measurement passes and constraints around transforms, SSR, and large lists.
What layout actually does
Framer Motion tracks elements with layout in a shared layout registry. When a render changes position or size due to flex/grid reflow, conditional rendering, or list reordering, Framer Motion records the before and after getBoundingClientRect values. It then animates from the old visual position to the new one using transform and opacity on the compositor thread.
This is FLIP: First, Last, Invert, Play. The measurement pass is required. The animation itself avoids layout thrashing, but the measurement still happens on every relevant render.
The prop is not just boolean. layout="position", layout="size", and layout="scale" constrain what is animated. layout="scale" is cheaper for simple containers because it scales the whole box, but it can distort children unless those children opt out with their own layout="position".
Practical pattern: reorderable list
A common supported case is a list where order is driven by state.
// React 18 + Framer Motion 11
import { motion, AnimatePresence } from 'framer-motion'
function List({ items }) {
return (
<motion.ul layout>
{items.map(item => (
<motion.li
key={item.id}
layout
layoutId={item.id}
>
{item.label}
</motion.li>
))}
</motion.ul>
)
}
Changing the items array order triggers automatic movement for each motion.li. No manual initial/animate values are needed. The same registry enables shared-element morphs when an element with the same layoutId appears in a different parent, such as thumbnail to detail.
Exit animations combine naturally with AnimatePresence. The leaving node stays in the DOM until the layout animation completes, which can interfere with virtualization libraries that unmount off-screen rows.
Performance and engineering trade-offs
Layout animations run on the compositor when possible, but they still require a layout measurement pass. In very large lists, >200 items on low-end devices, the measurement cost can cause jank. A practical guard is to disable layout for off-screen items and animate only the visible window.
Nested layout components without a shared layoutId create independent animation contexts. Parent CSS transforms, e.g., transform: scale on a wrapper, break the FLIP calculation and cause jumps.
Server rendering adds constraints. Layout measurements are unavailable at hydration. Framer Motion 11+ provides layoutRoot via MotionConfig to isolate measurement scopes and reduce hydration mismatches. Using layoutDependency or guarding with useLayoutEffect is a common pattern to avoid warnings.
Reduced motion is another consideration. Framer Motion 10+ respects prefers-reduced-motion by default, but the measurement overhead can remain. A global <MotionConfig reducedMotion="user"> can skip measurement entirely for those users.
When to prefer manual keyframes
Choose explicit keyframes when motion is designed, not derived. If you need precise easing, staggered delays, or animation of intrinsic content changes like text wrapping, layout may surprise you. Width/height changes driven by content wrapping are not animated unless the parent also has layout or the element uses layout="size".
Shared layoutId across routes requires the provider to sit above the router outlet. In Next.js App Router layouts, placing the registry too low breaks the morph.
A practical way to check the result is to render a minimal flex container with five <motion.div layout> children, shuffle order via state, and inspect the Performance tab for layout shift entries. Toggle layout="scale" vs layout="position" and inspect computed styles for child distortion during animation.
Use layout for layout-driven motion where correctness over time matters more than pixel-perfect control. Reserve manual keyframes for authored motion and for large, virtualized, or SSR-sensitive surfaces where measurement cost is a risk.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.