DEV Community

loading...
Cover image for Using Framer Motion to make page transitions in React

Using Framer Motion to make page transitions in React

sam_piggott profile image Sam Piggott ・5 min read

In this short tutorial, we'll be learning how we can use the AnimatePresence component provided by Framer Motion to create our very own page transitions when navigating between pages in React!

Animation Example

We'll be using React Router for this example - but this same principle should work for other router implementations, too (have tested with the Next.js router and it worked with no issues!)

Watch the Video Tutorial:

Video Tutorial

You can also download the source code from the link above, too!

Installing the required packages

First of all, let's install the required packages into our React project. We'll need React Router for navigation, and Framer Motion for the transition behaviour.

yarn add react-router-dom framer-motion
Enter fullscreen mode Exit fullscreen mode

Adding a BrowserRouter to our app

Next, in the top-level component where ReactDOM.Render is called (for me, index.tsx), we'll want to wrap our app's container in an instance of <Router> from react-router.

First, we'll add the necessary imports to index.tsx...

import { BrowserRouter as Router } from 'react-router-dom';
Enter fullscreen mode Exit fullscreen mode

Then, we'll wrap our top-level component in an instance of this router.

ReactDOM.render(
  <React.StrictMode>
    <Router> // <-- Adding this...
      <App />
    </Router> // <-- ...and this!
  </React.StrictMode>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

Building a simple switch

Next, we'll need to make some changes to the component immediately beneath the top-level component (in our example, the App component).

Inside the App component, we'll set up a basic routing system for our app. In this example, we're only going to be navigating between two simple pages - so we just need a <Switch> and two <Route> components to get us going.

So let's import those in our App.tsx file first...

import { Switch, Route } from 'react-router-dom';
Enter fullscreen mode Exit fullscreen mode

Then below, we'll return our simple switch from our App component. For clarity, Page1 and Page2 components can be any valid React component.

const App = () => {
  return (
    <Switch>
      <Route path="/page1">
        <Page1 />
      </Route>
      <Route path="/page2">
        <Page2 />
      </Route>
    </Switch>
  );
};
Enter fullscreen mode Exit fullscreen mode

Adding AnimatePresence from Framer Motion

Now, it's time to add the animation! First, we'll wrap our <Switch> in the <AnimatePresence> component from Framer Motion. Let's import that first...

import { AnimatePresence } from 'framer-motion';
Enter fullscreen mode Exit fullscreen mode

Then, let's wrap our app in that new component.

const App = () => {
  return (
    <AnimatePresence exitBeforeEnter initial={false}>
      <Switch>
        <Route path="/page1">
          <Page1 />
        </Route>
        <Route path="/page2">
          <Page2 />
        </Route>
      </Switch>
    </AnimatePresence>
  );
};
Enter fullscreen mode Exit fullscreen mode

Transitions with AnimatePresence only work when the child immediately below the AnimatePresence component changes. In that case, that's our Switch component. We'll need to add some props to help AnimatePresence recognise when that change has occurred.

Once again, in App.tsx, we're going to import the useLocation hook from react-router.

import { useLocation } from 'react-router-dom';
Enter fullscreen mode Exit fullscreen mode

And now, let's add the location and key props to our Switch.

const App = () => {
  const location = useLocation();
  return (
    <AnimatePresence exitBeforeEnter initial={false}>
      <Switch location={location} key={location.pathname}>
        <Route path="/page1">
          <Page1 />
        </Route>
        <Route path="/page2">
          <Page2 />
        </Route>
      </Switch>
    </AnimatePresence>
  );
};
Enter fullscreen mode Exit fullscreen mode

Adding the transition effect parameters to our page components

Great news - we're done with our App component. Now, let's add some animations to our pages, and we should be ready to go!

Here's the Page1 component I'm working with. It's super simple - just a simple div with some styles, and a Link to our other page in the app.

const Page1 = () => {
  return (
    <div style={{ ...styles.page, ...styles.page1 }}>
      <p style={styles.copy}>This is page 1</p>
      <Link style={{ ...styles.copy, ...styles.link }} to="/page2">
        Go to Page 2
      </Link>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

To trigger an animation when this Page1 component is mounted, we'll need to wrap it in a special component called motion.div which is provided by Framer Motion. So, let's import that...

import { motion } from 'framer-motion';
Enter fullscreen mode Exit fullscreen mode

Now, we'll wrap our Page1 component in our new motion.div component, and provide some props to perform the animation when it's mounted.

const Page1 = () => {
  return (
    <motion.div
      initial={{ scaleY: 0 }}
      animate={{ scaleY: 1 }}
      exit={{ scaleY: 0 }}
      transition={{ duration: 0.5 }}
    >
      <div style={{ ...styles.page, ...styles.page1 }}>
        <p style={styles.copy}>This is page 1</p>
        <Link style={{ ...styles.copy, ...styles.link }} to="/page2">
          Go to Page 2
        </Link>
      </div>
    </motion.div>
  );
};
Enter fullscreen mode Exit fullscreen mode

As you can see, we've added three new props to our motion.div component here, too.

  • initial - This is the style of the component at the beginning of the animation when it is animating in.
  • animate - This is the style of the component at the end of the animation when it is animating in.
  • exit - This is the style of the component at the end of the animation when it is animating out.
  • transition - Transition configuration. Here, we're specifying how long we want the duration to last for (in our case, 0.5 seconds).

With those props in place, we can expect the following behaviour:

  • When the prop is first mounted, it is invisible (scaleY: 0)
  • Then, it will immediately animate over 0.5 seconds to be visible (scaleY: 1).
  • When it is animating out, it will resize back down before it is removed from the DOM (scaleY: 0).

Finally, the only other thing we need to do is wrap our other page components that we wish to animate using the same method.

I'm animating between two pages (Page1 and Page2 in this example), so I'll need to wrap Page2 in a motion.div tag, too.

const Page2 = () => {
  return (
    <motion.div
      initial={{ scaleY: 0 }}
      animate={{ scaleY: 1 }}
      exit={{ scaleY: 0 }}
      transition={{ duration: 0.5 }}
    >
      <div style={{ ...styles.page, ...styles.page2 }}>
        <p style={styles.copy}>This is page 2</p>
        <Link style={{ ...styles.copy, ...styles.link }} to="/page1">
          Go to Page 1
        </Link>
      </div>
    </motion.div>
  );
};
Enter fullscreen mode Exit fullscreen mode

And we're done!

And with that, we are done! We have successfully set up a fancy animation when navigating between pages in React.

You should now also have all the knowledge you need to customise your transition styles, too. Each page can also have different transition styles - the sky's the limit!

CodeSnap

CodeSnap Preview

If you enjoyed this course, I'm uploading tutorial videos, courses, articles and plenty more. If you'd like to see more of this content, please consider signing up for the mailing list over on CodeSnap.io. It encourages me to make more videos and articles just like this one 🙏

Thanks for reading!

Discussion (0)

pic
Editor guide