framer-motion-layout

Installation
SKILL.md

Motion for React: layout animations

When to use this

  • A React component's size or position changes due to a state update (content growing, a flex/grid reorder, a conditional class) and the change should animate instead of snap.
  • You need a "shared element" transition between two different components or route views (a list thumbnail expanding into a detail page, a tab indicator sliding between tabs) using layoutId.
  • You need exit animations for components being removed from the React tree, which React itself does not support natively (AnimatePresence).
  • You're coordinating layout animations across a set of siblings that should move together in response to one of them changing (LayoutGroup).
  • Do NOT use this for drag/gesture-driven animation or scroll-linked value transforms; that is framer-motion-gestures-drag. Do NOT use this for imperative non-React JS animation; use motion-one-waapi outside of React.

Mental model

motion/react (the React-specific package from the Motion project, formerly branded Framer Motion) layout animation works by intercepting React's own render/commit cycle rather than watching the DOM for changes itself. When a motion.div has the layout prop and a re-render causes its measured bounding box to differ from before, Motion runs essentially the same First-Last-Invert-Play algorithm as GSAP Flip: it measures the box before the DOM update commits, lets React apply the real DOM/style change, measures again after, computes the delta as a transform, and animates from the inverted (old-position) transform back to identity. Because this hooks into React's lifecycle (via useLayoutEffect timing) rather than manual before/after calls, you get this behavior automatically just by adding the layout prop, without an explicit getState/from call pair.

layoutId extends this to elements that are not literally the same component instance, and can even be in entirely different parts of the tree (different routes, conditionally rendered branches). Any two motion elements sharing the same layoutId string are treated by Motion as "the same visual element" for animation purposes: when one unmounts and the other mounts (or both exist momentarily during a transition), Motion computes the transform delta between their two boxes and animates the surviving element smoothly between them, which is what produces the classic "shared element" effect (a card that visually flies from a grid position into an expanded modal, when in React terms they are actually two separate JSX elements).

LayoutGroup solves a real synchronization problem: when several sibling motion elements each have layout and one of them changes size, the OTHERS should also animate their resulting reposition, not simply snap to their new positions while only the triggering element animates smoothly. LayoutGroup scopes a shared layout-animation coordination context so that a batch of layout changes triggered by one state update are measured and animated together in the same "flip" pass, keeping the group visually coherent (e.g. an accordion where opening one item pushes siblings down, and the siblings should slide, not jump).

AnimatePresence exists because React's reconciler, by the time it removes a component from the tree, has already unmounted it; there is no native hook for "animate before actually removing this DOM node." AnimatePresence works by intercepting the unmount: when a child with an exit prop would be removed, AnimatePresence keeps it mounted in the DOM, plays its exit animation, and only then actually removes it. Its mode prop controls how this interacts with a simultaneously-entering replacement: 'sync' (default) animates enter and exit at the same time, 'wait' waits for the exiting element to fully finish before mounting/animating the entering one, and 'popLayout' removes the exiting element from document flow immediately (via absolute positioning) so surrounding layout can reflow into its space while it finishes animating out, rather than the exiting element continuing to occupy layout space during its own exit.

Layout-thrash pitfalls arise because layout animation requires the FLIP measurement pass on every relevant re-render; components with expensive render trees, or a layout prop applied too broadly (e.g. on every item of a very large list when only one item's size actually changed), force repeated measurement passes that add up. This is a real, measurable cost, distinct from ordinary React re-render cost.

Installs
2
First Seen
Aug 30, 2026
framer-motion-layout — avnehsbhatia/ultraui