# How I use shared layout animations

> How I use shared layout animations.
>
> Source: https://jakub.kr/work/shared-layout-animations. Interactive demos are only available on the page.

Shared layout animations in [Motion](https://motion.dev/docs/react-layout-animations) are *very* powerful. They let you smoothly animate between different elements across different states with a single line of code.

## Single element

To use shared layout animations, first turn the element you want to animate into a `motion` component and add the `layoutId` prop.

In this example, a switch statement renders the same circle in different positions based on the selected tab.

```tsx title="shared-layout-tabs.tsx"
const getTabContent = () => {
    switch (activeTab) {
      case "Design":
        return (
            <motion.div
              className="size-16 rounded-full bg-gray-1200"
              layoutId="circle"
            />
        );
      case "Engineering":
        return (
          <motion.div
            className="absolute top-4 right-4 size-16 rounded-full bg-gray-1200"
            layoutId="circle"
          />
        );
      case "Product":
        return (
          <motion.div
            className="absolute top-4 left-4 size-16 rounded-full bg-gray-1200"
            layoutId="circle"
          />
        );
      case "Marketing":
        return (
          <motion.div
            className="absolute bottom-4 left-4 size-16 rounded-full bg-gray-1200"
            layoutId="circle"
          />
        );
      case "Sales":
        return (
          <motion.div
            className="absolute right-4 bottom-4 size-16 rounded-full bg-gray-1200"
            layoutId="circle"
          />
        );
      default:
        return null;
    }
  };
```

You can see that we don't manipulate an individual component. Instead, we render different components in different places in different states that happen to look the same. Their appearance can also be different, but we'll cover that later.

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.

```tsx title="shared-layout-circle.tsx"
<motion.div
  className="size-16 rounded-full bg-gray-1200"
  layoutId="circle"
/>
```

The way this works under the hood is by using the [FLIP](https://css-tricks.com/animating-layouts-with-the-flip-technique/) technique, which stands for `First`, `Last`, `Invert`, `Play`.

If you're interested in how it works, I recommend reading [Inside Framer's Magic Motion](https://www.nan.fyi/magic-motion) by [Nanda](https://x.com/nandafyi), a beautiful article that explains the concept really well.

## Multiple elements

You can animate between multiple elements too.

As long as the elements share the same `layoutId` and that `id` is unique within the group, the animation will work correctly.

```tsx title="multiple-layout-ids.tsx"
<div className="relative flex h-full w-full items-center justify-center gap-8">
  <motion.div
    className="size-16 rounded-full bg-orange-500"
    layoutId="circle-1"
  />
  <motion.div
    className="size-16 rounded-full bg-blue-500"
    layoutId="circle-2"
  />
  <motion.div
    className="size-16 rounded-full bg-green-500"
    layoutId="circle-3"
  />
</div>
```

If you assign the same `layoutId` to multiple elements in the same state, the animation will break, like in the example below.

Now the only remaining thing is to animate the arrows between the circles to match the animation from the beginning of the article.

The arrows are wrapped in `AnimatePresence` so they can fade in and out as the layout changes. The circles stay outside of `AnimatePresence`, since we don't want them to fade in and out.

```tsx title="arrow-presence.tsx"
<AnimatedCircle layoutId="circle-1" />

<AnimatePresence>
  <motion.div
    animate={{ opacity: 1, filter: "blur(0px)" }}
    exit={{ opacity: 0, filter: "blur(4px)" }}
    initial={{ opacity: 0, filter: "blur(4px)" }}
    key="divider-1"
  >
    <ArrowMarquee arrowCount={8} />
  </motion.div>
</AnimatePresence>

<AnimatedCircle layoutId="circle-2" />
```

Each arrow wrapper has a `key` that tells `AnimatePresence` to run the `exit` animation when the element unmounts and the `initial` animation when it mounts.

## Changing values between states

The elements you animate between can have different property values like `height`, `width`, `position` and more.

In this example, the elements have different positions, sizes and border radii. The border radius is passed to `animate` instead of a `className` so Motion can smoothly interpolate between the values.

```tsx title="card-layout.tsx"
const getCardConfig = () => {
  switch (activeTab) {
    case "Design":
      return {
        className: "top-24 left-46 h-12 w-32",
        borderRadius: "8px",
      };
    case "Engineering":
      return { className: "top-10 right-2 h-78 w-32", borderRadius: "8px" };
    case "Product":
      return { className: "bottom-2 left-2 h-20 w-28", borderRadius: "8px" };
    case "Marketing":
      return { className: "top-17 left-49 size-16", borderRadius: "4px" };
    case "Sales":
      return { className: "bottom-2 right-2 size-8", borderRadius: "32px" };
  }
};

<motion.div
  className={cn("absolute bg-gray-1200", cardConfig.className)}
  animate={{ borderRadius: cardConfig.borderRadius }}
  layoutId="card"
/>
```

`AnimatePresence` is also used to animate the content behind the moving element.

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.

If they are inside, the `initial` and `exit` animations will trigger while the elements are animating. This doesn't look great, especially if you are animating properties like `opacity`.

## Other examples

There are many other creative examples of shared layout animations. One of my personal favorites is a preview component like the one below. There are also other great examples in the [Motion](https://motion.dev/docs/react-layout-animations) documentation.

I used shared layout animations paired with the [Base UI Dialog component](https://base-ui.com/react/components/dialog). The idea is the same as in the other examples. There are two separate components: a card and a dialog. Motion animates between them using a shared `layoutId`.
