Morph with a shared layoutId

Share a layoutId so one component morphs into the other.

Jakub Krehel@jakubkrehel
A demo component I built for my article about shared layout animations
Jan 12, 2026View on X ↗
Jakub Kreheljakub.kr
All that is necessary for these elements to animate is that they share the same layoutId. Motion does all the heavy lifting to animate between them.
How I Use Shared Layout AnimationsRead article ↗
Jakub Kreheljakub.kr
When combining AnimatePresence and shared layout animations, keep components with layoutId outside of AnimatePresence if you don’t want the initial and exit animations to affect them during the layout transition.
How I Use Shared Layout AnimationsRead article ↗

From design-motion-principles, cookbook §6 Shared Layout Animations.

Card into modal

Open a card, close it, or try Rapid.

New modal fades inBad
opacity 0→1, scale .96→1
Shared layoutIdGood
layoutId={`card-${id}`}

In or out of AnimatePresence

Expand, or drag Scrub to freeze mid-morph.

In AnimatePresenceBad
initial={{ opacity: 0 }} exit={{ opacity: 0 }}
Out of AnimatePresenceGood
layoutId="player"

Tab underline

Tap a tab, or add one with a longer name.

One underline per tabBad
opacity
One shared underlineGood
layoutId="underline"

Why it matters, pros and cons

Why it matters

Seeing the card become the modal shows where it came from and where it returns.

Pros

  • Origin and destination stay obvious
  • Interruptible from any on-screen position
  • Transform-only, stays on the compositor

Cons

  • Scale distorts radius and children unless corrected
  • Duplicate layoutIds morph into each other
  • Skip when the origin is off screen