Animating React Components Using React Transition Group

Yusuff Faruq

Animations help improve the feel of a website or web app and this often leads to better user experience. We can use animations during different events: during page transitions, while scrolling and of course during mounting and unmounting of components in component-based frameworks or libraries such as React.

In this article, you’ll learn how to trigger animations/transitions during the mounting and unmounting stages of your React component using React Transition Group.

According to the React Transition Group Documentation, it’s not an animation library and I agree with this. It simply exposes transition stages, manages classes and performs other useful functions which make it easy to trigger animations during mounting and unmounting. This “pseudo-library” consists of 4 components, three of which we will use in this article.

Let’s get started!

The Transition Component

According to the documentation:

The Transition component lets you describe a transition from one component state to another over time with a simple declarative API. Most commonly it’s used to animate the mounting and unmounting of a component, but can also be used to describe in-place transition states as well.

You’ll want to use the Transition component when you need to animate the mounting and unmounting of a component using styling in JavaScript. If you want to use CSS styling, you should use the CSSTransition component which we’ll look into next.

Once again, according to the docs:

By default the Transition component does not alter the behavior of the component it renders, it only tracks “enter” and “exit” states for the components. It’s up to you to give meaning and effect to those states.

Now, we will demonstrate how the Transition Component tracks the different states of a component.

First, let’s create a new React project. Once that is done, we’ll need to install React Transition Group.

With npm:

$ npm install react-transition-group --save

Or using Yarn:

$ yarn add react-transition-group

Now that you have it installed, we need to create a new component. I’ll call mine AComponent. Note that I will be writing all my code in one file: app.js.

Once you have your empty component ready, import the Transition component from react-transition-group.

import { Transition } from 'react-transition-group';

Now that we’ve it imported, we can use it in our component. The Transition component takes a function as a child and this function returns whatever markup we want to apply animations to. The function also has an argument called state. This state argument tells us the present state of our component such as entering, entered, exiting and exited. Now that we understand that, let us write some code.

App.js

const AComponent = ({ in: inProp }) => (
  <Transition in={inProp} timeout={500}>
    {state => (
      <div>
        I am {state}
      </div>
    )}
  </Transition>
); 

As you can see, the Transition component takes in two props - an in prop and a timeout prop. There are other props some of which we will look into later. For now, let’s discuss these two props:

  • The value of the in prop is a boolean (true or false). If the value is true, our component will enter the entering state; if it is false, our component will enter the exited state. We will set this using state soon.
  • The timeout prop defines the duration for each transition. You can assign it just one value if you want to set the same duration for all transitions. By transitions, I mean appear, enter, and exit. You can also assign different time duration to each transition like this:
timeout={{
 appear: 500,
 enter: 300,
 exit: 500,
}}

Now that we have written our component, let us use it in our App component. In our App component, we will create a button which will be used to toggle the value of the in prop between true and false. To do that, we’ll make use of the useState hook. First of all, we need to import the useState hook from React:

App.js

import React, { useState } from 'react';

Once that is done, let’s create a new state:

const [entered, setEntered] = useState(false);

We can write our button element as follows:

App.js

<button
  onClick={() => {
    setEntered(!entered);
  }}
>
  Toggle Entered
</button>

This button will toggle the value of the in prop.

Now let us use AComponent in our App component

App.js

function App(){
  const [entered, setEntered] = useState(false);
  return (
    <div
>
      <AComponent in={entered} />
        <button
          onClick={() => {
            setEntered(!entered);
          }}
        >
          Toggle Entered
        </button>
    </div>
  );
}

Now, once you run your React app, you should see a text with “I am exited”. This is because we set the initial value of our state variable, entered, to false. Clicking the button should change the text to “I am entering” and then to “I am entered”. Clicking again should change the text to “I am exiting” and then to “I am exited”. You should now understand how tracking state with the Transition component works. By now, you have probably noticed whenever a component exits, it doesn’t unmount. You change this by adding the unmountOnExit prop to the Transition component like this:

App.js

const AComponent = ({ in: inProp }) => (
  <Transition in={inProp} timeout={500} unmountOnExit>
    {state => (
      <div>
        I am {state}
      </div>
    )}
  </Transition>
); 

Now, the component should actually be unmounted upon reaching the “exited” stage. Since we now understand how tracking of state works, we should be able to apply styles during mounting and unmounting. Now let’s make AComponent scale up in size in during mounting. We need to change the initial value of entered to true. We also need to define some default styles and set the different styles during each state of the component. First of all, let us define our default styles:

App.js

const defaultStyle = {
  transition: `transform 200ms, opacity 200ms ease`,
  opacity: 1
};

Now let’s define the different styles for each state:

App.js

const transitionStyles = {
  entering: { transform: 'scale(0.5)', opacity: 0 }, 
  entered: { transform: 'scale(2.0)', opacity: 1},
  exiting: { opacity: 0 },
  exited: { opacity: 0 }
};

Normally, AComponent would move right into the entered state without passing through the entering state on first mount. To change that, we need to add a prop called appear to the Transition component used in AComponent. We also want to use a different time duration for each state. We’ll set appear to 100ms and enter and exit to 300ms. Finally, we want to apply the default and transition styles to the div element returned from the child function of the Transition Component. AComponent should look like this now:

App.js

const AComponent = ({ in: inProp }) => (
  <Transition
    in={inProp}
    timeout={{
      appear: 100,   
      enter: 300,
      exit: 300
    }}
    appear
    unmountOnExit
  >
    {state => (
      <div
        style={{
          ...defaultStyle,
          ...transitionStyles[state]
        }}
      >
        I am {state}
      </div>
    )}
  </Transition>
);

Now when you run your app, the text should scale up in size during mounting and scale down before vanishing during unmounting with a nice transition.

The CSSTransition Component

This transition is very similar to the Transition but it uses CSS transitions instead of JavaScript styles. With the CSSTransition component, you have more control over the different states of your component.

CSSTransition appends different classes during different states to your defined base class names. This allows you to style your component differently during each state. Using CSSTransition, we’re going to create a component which renders a text that rotates in during mounting and rotates out during unmounting. It’s a simple demonstration but it shows how CSSTransition works. Let us begin by importing the CSSTransition component.

App.js

import { Transition, CSSTransition } from 'react-transition-group';

After that’s done, we need to create a new component which I will call Gator. Gator will return a CSSTransition component whose child will be the markup of our component. We want our CSSTransition component to transition on first mounting so we will add the appear prop. We also want our component to actually leave the DOM after reaching the exiting stage so we will add the unmountOnExit prop.

For our timeout prop, we want to set 0 milliseconds for the appear state, 0 milliseconds for the enter state and 300 milliseconds for the exit state. Since CSSTransition uses all the props from the Transition component, we also have to set an in prop. The value of this in prop will be destructured from the props object and aliased as inProp. In addition to the other props we have seen in the Transition component, CSSTransition also has a classNames prop which is used to define the base class name. We will set this to “roll”. This means several classes like “roll-appear”, “roll-appear-active” and so on will be applied to our component depending on the state of the component.

According to the docs:

CSSTransition applies a pair of class names during the appear, enter, and exit states of the transition. The first class is applied and then a second *-active class to activate the CSS transition. After the transition, matching *-done class names are applied to persist the transition state.

Now, Gator should look like this:

App.js

const Gator = ({ in: inProp }) => {
  return (
    <CSSTransition
      unmountOnExit
      in={inProp}
      timeout={{ appear: 0, enter: 0, exit: 300 }}
      classNames='roll'
      appear
    >
      <div>Gator</div>
    </CSSTransition>
  );
};

Since we’re going to be using CSS transitions, we obviously need to write CSS. I’ll be writing my CSS in my App.css file. We want the div containing the text “Gator” to rotate 720 degrees and scale up in size during mounting. Firstly, we need to set the initial opacity which we want to be 0:

App.css

.roll-appear{
  opacity:0;
}
.roll-enter{
  opacity:0;
}

Now we need to set the styles for the element after it has been mounted. The component will transition from the initial styles to this style:

App.css

.roll-enter-done{
  transform: rotate(720deg) scale(3);
  opacity:1;
  transition: transform 1000ms, opacity 1000ms;
}

We also want the component to rotate out and scale down in size during unmounting. So let’s define the style at the beginning of the exit state. This will be similar to the styles applied by .roll-enter-done:

App.css

.roll-exit{
  transform: rotate(720deg) scale(3);
  opacity:1;
}

Let’s define the styles during exiting before the component gets unmounted:

App.css

.roll-exit-active{
  transform:rotate(0deg) scale(1);
  opacity: 0;
  transition: transform 1000ms, opacity 1000ms;
}

Now let us replace AComponent in our App component with Gator. We will also set the value of the in prop to entered.

App.js

function App() {
  const [entered, setEntered] = useState(true);
  return (
    <div
>
      <Gator in={entered} />
      <button
        onClick={() => {
          setEntered(!entered);
        }}
        style={{ marginTop: '10rem' }}
      >
        Toggle Entered
      </button>
    </div>
  );
}

Finally, we can run our app to see if the transition worked. If you followed all the steps, the div should rotate 720 degrees in and scale up in size and rotate back to zero degrees while scaling down in size during unmounting.

The TransitionGroup Component

The last component I want to discuss in this article is the TransitionGroup component.

There is not much to say about this component except that it manages a set of transition components, like a group of Transition or CSSTransition components. An example of where you would use this component is in a TodoList component for animating the creation and deletion of todo items.

Conclusion

We’ve finally come to the end of this article. I trust you’ve learned how React Transition Group works and how flexible you can be with it. If you need more information about React Transition Group, you can always visit the documentation here.

  Tweet It

🕵 Search Results

🔎 Searching...