Edit Page

#AnimateSharedLayout

Animate between different components that share a layout ID.
Note: This feature is currently in beta. You can install the latest Framer Motion 2 beta using npm install framer-motion@beta.

AnimateSharedLayout allows animation between seperate components.

These components do not need to be rendered in the same hierarchy, as long as they share the same layoutId, and are wrapped within the same AnimateSharedLayout component.

This can be used to create a variety of effects, like this underline selection:

Or fully route-driven shared element transitions (try pressing back from the overlay):


#Usage

#Shared layout animations

When a new component is added with an existing layoutId, on mount it will visually animate out from the previous component that had or has the same layoutId.

import { motion, AnimateSharedLayout } from "framer-motion"

export const MyComponent = ({ items, selectedId }) => (
  <AnimateSharedLayout>
    {items.map(item => (
      <li>
        {item.title}
        {item.id === selectedId && (
          <motion.div layoutId="underline" />
        )}
      </li>
    ))}
  </AnimateSharedLayout>
)

If the previous component still exists in the tree, it'll automatically be hidden using opacity: 0, but still occupy space in the layout.

#Exit animations

When a component that's expected to enter and exit the tree is wrapped in AnimatePresence, it'll automatically animate back to the original component with its layoutId when it's removed.

Open example in CodeSandbox
<AnimateSharedLayout>
  {images.map((img) => <motion.img layoutId={img.id} />)}
  <AnimatePresence>
    {selectedId && <motion.img layoutId={selectedId} />)}
  </AnimatePresence>
</AnimateSharedLayout>

#Crossfade

Sometimes, the components that you are animating from/to might look different enough that when an entering/exiting component is added or removed there is an obvious visual switch. For instance, a text box might be wrapped differently in one layout than another.

By setting type="crossfade", both the exiting and previous components will animate together. The root components in each stack that has either animate={true} or a layoutId will crossfade, so one gets hidden as the other is revealed.

Open example in CodeSandbox
<AnimateSharedLayout type="crossfade">
  {images.map((img) => <motion.img layoutId={img.id} />)}
  <AnimatePresence>
    {selectedId && <motion.img layoutId={selectedId} />)}
  </AnimatePresence>
</AnimateSharedLayout>

#Transition customisation

The transition used for the shared layout animation is defined in the component's transition prop.

This accepts all the same Transition settings as usual. Currently it is only possible to set a single transition for all child components. Using individual values per child component is not supported. This limitation will be removed in a future release.

<AnimateSharedLayout transition={{ duration: 2 }}>

#Correcting visual distortion

For maximum performance, layouts are animated using the transform property. Animating width and height using scaleX and scaleY can introduce visual distortion on children.

This can be automatically corrected by applying animate to those children.

<motion.div sharedId="container">
  <motion.div animate />
</motion.div>