React-Router: Animated Transitions DIY
Tags: react, reactrouter, css, animation
Published: 2021-04-19 06:53:27

> This post is based on React Router v5.

### What's the issue?

React Router is a great library for SPA navigation, but it has one small problem when we need a transition/animation effect while we navigation between pages - React Router, by default, changes to a new route instantly, so the element does not have time to play the transition effect.

### What we need to do?

* Prevent component from unmounting before the 'Out' animation is completed.
* Switch component when the 'In' animation is start.

So the transition stages will be:

Link clicked -> prevent the route switching -> 'Out' animation start -> 'Out' animation end -> switch to new route -> 'In' animation start -> 'In' animation end - > done  


### Let's start!

First, let's create the App component:

```javascript
export default function App() {
  return (
    <BrowserRouter>
      <div className={`App`}>
        <nav>
          <Link to='/'>Home</Link>
          <Link to='/other'>Other</Link>
        </nav>
        <Content />
      </div>
    </BrowserRouter>
  );
}

```

Then, the Content component:

```javascript
function Content() {
  return (
    <div>
      <Switch>
        <Route path="/other">
          <section>other</section>
        </Route>
        <Route path="/">
          <section>home</section>
        </Route>
      </Switch>
    </div>
  );
}

```

Now, we need to stop route from switching. By default, the`<Switch/>` will use the current url for matching the route, but we can stop it from doing that by assigning it a `Location`.

```javascript
  <Switch location={SomeLocation}>
```

We will need a state to keep the current location before 'Out' animation finish, and we can assign the current location as the default value. we can use `useLocation` to get the current location.

```javascript
  ...
  const location = useLocation();
  const [displayLocation, setDisplayLocation] = useState(location);
  ...
  <Switch location={displayLocation}>
```

Now, if you click the `Link`, you will notice that even the URL is changed, the content stay the same.

Next, we need add a state for controlling the stage of the transition.

```javascript
  const [transitionStage, setTransistionStage] = useState("fadeIn");
``` 
Then, we can use `useEffect` to check if location is changed, and start the 'fadeOut'.

```javascript
  useEffect(() => {
    if (location !== displayLocation) setTransistionStage("fadeOut");
  }, [location]);
```

Finally, we need a way to update the stage and location when animation is over. For that we can use the `onAnimationEnd` event.

```javascript
function Content() {
  ...
  return (
    <div
      className={`${transitionStage}`}
      onAnimationEnd={() => {
        if (transitionStage === "fadeOut") {
          setTransistionStage("fadeIn");
          setDisplayLocation(location);
        }
      }}
    >
    ...
    </div>
  );
}
``` 

And, Here is the demo and the finished code:
{% codesandbox md6mi%}

```javascript
import { createContext, useContext, useEffect, useRef, useState } from "react";
import {
  BrowserRouter,
  Redirect,
  Route,
  Switch,
  Link,
  useLocation
} from "react-router-dom";
import "./styles.css";

export default function App() {
  return (
    <BrowserRouter>
      <div className={`App`}>
        <nav>
          <Link to="/">Home</Link>
          <Link to="/other">Other</Link>
        </nav>
        <Content />
      </div>
    </BrowserRouter>
  );
}

function Content() {
  const location = useLocation();
  const [displayLocation, setDisplayLocation] = useState(location);
  const [transitionStage, setTransistionStage] = useState("fadeIn");

  useEffect(() => {
    if (location !== displayLocation) setTransistionStage("fadeOut");
  }, [location]);

  return (
    <div
      className={`${transitionStage}`}
      onAnimationEnd={() => {
        if (transitionStage === "fadeOut") {
          setTransistionStage("fadeIn");
          setDisplayLocation(location);
        }
      }}
    >
      <Switch location={displayLocation}>
        <Route path="/other">
          <section>other</section>
        </Route>
        <Route path="/">
          <section>home</section>
        </Route>
      </Switch>
    </div>
  );
}

```


Thank you all!