Declarative animations with react-pose

12-06-2018

In general animating the view is hard, because suddenly the state is dependent on time. Views can exist longer than they existed before because of exit animations, or some layouts must wait for other elements to take their place. And animations are expensive, so don’t do too much and whenever possible you should use hardware accelerated key-frame animations or transitions. But sometimes the animations get a little more complex and are difficult to handle with pure CSS and style rules. Then it’s time to take some animation library to help.

One of the large goals of react is to describe as much of the app in a declarative way, for styling the styled-components workflow is working really good and react-pose has a similar API to animate components. Instead of styles you define different “poses” the component can have and define how the transitions of values between the poses should work.

For example a sidebar component which can be hidden on the left side of the screen would look like this:

import posed from 'react-pose'

const Sidebar = posed.nav({
  open: {
    x: '0%',
    // animate child components with 100ms between entries
    staggerChildren: 100,
  },
  closed: { x: '-100%' },
})

const NavItem = posed.li({
  open: { opacity: 1 },
  closed: { opacity: 0 },
})

export default ({ isOpen, navItems }) => (
  <Sidebar pose={isOpen ? 'open' : 'closed'}>
    <ul>
      {navItems.map(({ url, name }) => (
        <NavItem>
          <a href={url}>{name}</a>
        </NavItem>
      ))}
    </ul>
  </Sidebar>
)

If you want to have enter and exit animations you need a Wrapper Component managing newly created and disappearing components. With react-pose you can use the PoseGroup to manage posed components. Every direct child of a PoseGroup will get the poses enter and exit and needs a unique key prop to enabled tracking of the elements.

const Item = posed.div({
  enter: { opacity: 1 },
  exit: { opacity: 0 },
})

const ItemList = ({ items }) => (
  <PoseGroup>
    {items.map(item => (
      <Item key={item.id} />
    ))}
  </PoseGroup>
)

As an exercise try to rebuild this animation from dribble, a working result could look like this:

You can use this repository as a basis.

Tasks

  • identify all moving elements in the dribble design
  • define poses for all UI states
  • define transitions for all pose changes