Hero animations in React with react-motion-layout
Tags: transition, react, hero
Published: 2020-03-27 00:52:23


Hello Devs.

A couple of days ago I published my first React package and I want to show you how to use it.

## React-Motion-Layout
This library helps you animate components from two different React trees. In other words, to create Hero Animations. It's compatible with moderns browsers and uses the Element.animate() Web API.

Let's build one of my favorite examples, a photo gallery.

#### This is the final result
*Click on any photo to see it in action.*

{% codesandbox b6gkm %}

Looks beautiful right? Let's take a look at how simple is to recreate this example.

### 1 - Create placeholder photos
Thanks to Unsplash for those amazing photos.
```javascript
// PhotosDB.js
export default [
  {
    photo:
      "https://images.unsplash.com/photo-1474313438662-85ce389c174a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=50"
  },
  {
    photo:
      "https://images.unsplash.com/photo-1521170665346-3f21e2291d8b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=50"
  },
  {
    photo:
      "https://images.unsplash.com/photo-1520512202623-51c5c53957df?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=50"
  },
];
```

### 2 - Let's wrap our app with MotionLayoutProvider
Motion Layout Provider is responsible for providing the state management.
```jsx
// App.js
...
export default function App() {
  return (
    <Router>
      <MotionLayoutProvider>
        <Switch>
          <Route path="/photo/:photoId">
            <Photo />
          </Route>
          <Route path="/">
            <Photos />
          </Route>
        </Switch>
      </MotionLayoutProvider>
    </Router>
  );
}
```

### 3 - Create The Photos Component

Since this is an individual screen, we'll wrap it using MotionScreen to clean registered elements when abandoning this screen.
```jsx
import { MotionScreen } from 'react-motion-layout';
export default function Photos() {
  return (
    <MotionScreen>
      <div className="flex flex-wrap">
         {PhotosDB.map((item, id) => (
           <ItemComponent item={item} id={id} key={id} />
         ))}
      </div>
    </MotionScreen>
  );
}
```

### 4 - The single photo item
Each item will be wrapped with a  **MotionScene**. A  **MotionScene**  is a component that contains  **SharedElements**.

**SharedElements**  are the components that we will animate. They must have an unique key called  _animationKey_, we use that key to find a matching SharedElement when changing the views.

----------

**MotionScene**  accepts an onClick property, in this case we are using the  **withTransition**  hook, that will trigger the animation and then will change the route using the history hook provided by react-router-dom.
```jsx
...
import { useMotion, MotionScene, SharedElement } from 'react-motion-layout';

// PhotoItem.js
export default function ItemComponent({ item, id }) {
  const history = useHistory();
  const withTransition = useMotion(`photo-${id}`);
  const callback = useCallback(() => history.push(`/photo/${id}`), [
    history,
    id
  ]);

  return (
    <MotionScene name={`photo-${id}`} onClick={withTransition(callback)}>
      <div className="p-4 cursor-pointer hover:bg-gray-100">
        <SharedElement.Image
          className="w-64"
          alt=""
          src={item.photo}
          animationKey="image"
        />
      </div>
    </MotionScene>
  );
}
```

### 5 - The individual photo view
The Story View is wrapped by a MotionScreen since it represents a single screen. And of course, it could contain more than a single Scene.

Since it's just one scene, we will wrap it with MotionScene as well, when navigating, those scenes will match and the Package with look for the declared SharedComponents and match them using its keys. then, it will perform the animation.
```jsx
...
import { useParams } from "react-router-dom";
import PhotosDB from "./PhotosDB";

import { MotionScene, MotionScreen, SharedElement } from "react-motion-layout";

export default function Photo() {
  const { photoId } = useParams();
  const item = PhotosDB[photoId || 0];

  return (
    <MotionScreen>
      <MotionScene name={`photo-${photoId}`}>
        <div className="flex flex-col p-8">
          <SharedElement.Image
            className="w-64"
            alt=""
            src={item.photo}
            animationKey="image"
          />
        </div>
      </MotionScene>
    </MotionScreen>
  );
}
```

##   And that's it

Now when you click on any item of the gallery it should animate using the shared components we'd just defined.

[Motion Layout Docs](https://motion-layout.azurewebsites.net/)
[Github](https://github.com/jeffersonlicet/react-motion-layout)
[Example using Text](https://codesandbox.io/s/chat-example-dyyy1)

### Thanks.