๐Ÿ“ฝ๏ธList Animation

Adding animations to list items in React using the TransitionGroup component from react-transition-group library.

The <TransitionGroup> component manages a set of transition components (<Transition> and <CSSTransition>) in a list. Like with the transition components, <TransitionGroup> is a state machine for managing the mounting and unmounting of components over time.

Note that <TransitionGroup> does not define any animation behavior! Exactly how a list item animates is up to the individual transition component. This means you can mix and match animations across different list items.

<TransitionGroup> renders a <div> by default. You can change this behavior by providing a component prop. If you use React v16+ and would like to avoid a wrapping <div> element you can pass in component={null}. This is useful if the wrapping div borks your css styles.

type: any

default: 'div'

A convenience prop that enables or disables enter animations for all children. Note that specifying this will override any defaults set on individual children Transitions.

type: boolean

A convenience prop that enables or disables exit animations for all children. Note that specifying this will override any defaults set on individual children Transitions.

type: boolean

Example

Let's make a list of numbers which we can add and remove based on click handlers.

components/List.js
import {useState} from 'react';

import classes from "./List.module.css";

const List = () => {
  const [data, setData] = useState({
    items: [1, 2, 3]
  });

  const addItemHandler = () => {
    setData((prevState) => {
      let value = 1;
      if (prevState.items.length > 0) {
        value = prevState.items[prevState.items.length - 1] + 1;
      }
      return {
        items: prevState.items.concat(value)
      };
    });
  };
  const removeItemHandler = (idx) => {
    setData((prevState) => {
      return {
        items: prevState.items.filter((item, index) => index !== idx)
      };
    });
  };

  let listContent;
  if (data.items.length === 0) {
    listContent = <p>Please add items!</p>;
  } else {
    listContent = data.items.map((item, index) => (
      <li
        key={index}
        className={classes.listitem}
        onClick={removeItemHandler.bind(null, index)}
      >
        {item}
      </li>
    ));
  }

  return (
    <div className={classes.container}>
      <button className="button" onClick={addItemHandler}>
        Add Item
      </button>
      <p>Click to remove the item.</p>
      <ul className={classes.list}>{listContent}</ul>
    </div>
  );
};

export default List;

Since the <TransitionGroup> component does not define any animation behavior, we will use the <CSSTransition> component to define the animations in our list items!

The important thing here is, we are not going to use the in property to control the state of our transition. The special thing about <TransitionGroup> is that it is able to handle multiple items, and is able to determine whenever one element changes, i.e, if it is removed or added. It will then manually set the in property on the wrapped <Transition> or <CSSTransition>component. We cannot control the in property for a dynamic list, hence <TransitionGroup> does that for us out of the box.

Code snippet

components/List.js
//...
import TransitionGroup from 'react-transition-group/TransitionGroup';
import CSSTransition from 'react-transition-group/CSSTransition';

const List = () => {
  //...
  const listItem = listArr.items.map((item, index) => (
    <CSSTransition
      key={index}
      timeout={200}
      classNames={{
        enter: classes['fade-enter'],
        enterActive: classes['fade-enter-active'],
        exit: classes['fade-exit'],
        exitActive: classes['fade-exit-active'],
      }}
    >
      <li
        className={classes.ListItem}
        onClick={removeItemHandler.bind(null, index)}
      >
        {item}
      </li>
    </CSSTransition>
  ));
  
  return (
    <div>
      <button className="button" onClick={addItemHandler}>
        Add Item
      </button>
      <p>Click to remove the item.</p>
      <TransitionGroup component="ul" className={classes.List}>
        {listItems}
      </TransitionGroup>
    </div>
  );
}

export default List;

The final code example can be found here at codesandbox.io.

Alternatives

There are a few popular alternatives to react-transition-group for animating a React app:

To use react-transition-group for route animations, refer to this example.

All of them are open-sourced, and have their own docs, if you would like to explore them.

Last updated