React Router - Animated Transitions

In this article, I will show you how to do animated transitions using react-router and react-transition-group libraries and the solution for the common mistake.

Here is the sample application with animated transitions below

React Router Transitions

In this sample application, I used <TransitionGroup> and <CSSTransition> for animation.

From the docs, <TransitionGroup> is a state machine for managing the mounting and unmounting of components over time.

In our case, whenever the route changes, <TransitionGroup> and <CSSTransition> will apply transition animations. Let's review the code below

<TransitionGroup>
      <CSSTransition key={location.pathname} timeout={500} classNames="fade">
        <Switch location={location}>
          <Route exact path="/products" component={Products} />
          <Route exact path="/services" component={Services} />
          <Route path="*" component={Home} />
        </Switch>
      </CSSTransition>
    </TransitionGroup>
AnimationApp.tsx

By design, <CSSTransition> will prefix -enter, -enter-active and -exit to the classnames prop in runtime. We can use these class names to implement our animation effect as shown below

.fade-enter {
  opacity: 0;
  z-index: 1;
}

.fade-enter.fade-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

.fade-exit {
  opacity: 0;
}
styles.css

For more details about <CSSTransition>, refer to the document here

One common issue is that during transition, both the new <CSSTransition> and old <CSSTransition> component are in the DOM. This issue occurs as Switch component will have same location prop during transition and hence, rendering both components.

To solve this issue, you need to get location from react-router-dom and pass it manually to Switch as shown below

const AnimationApp = () => {
  const location = useLocation();

  return (
    <TransitionGroup>
      <CSSTransition key={location.pathname} timeout={500} classNames="fade">
        <Switch location={location}>
          <Route exact path="/products" component={Products} />
          <Route exact path="/services" component={Services} />
          <Route path="*" component={Home} />
        </Switch>
      </CSSTransition>
    </TransitionGroup>
  );
};
AnimationApp.tsx

And just like that, it works.