Back to overview

Adding route transition animations in Remix

| 3 min read

TLDR:

Have a look at the live site at thomasledoux.be. Code can be found on Github.

When I rebuilt my website using Remix, I didn’t bother putting any time into animating anything. After the rebuilt has been live for some weeks, I wanted to add some more fun things, so the first thing that came to mind was animations.

Remix makes it really easy to add transitions to your routes when doing data mutations, using the useTransition() hook. But what I wanted, is just animating in the route which is requested by clicking a link, and animating out the currently active route. The easiest way I found to do this, is using Framer Motion. By wrapping all of my content with the <AnimatePresence /> component, we’re now ready to add the actual animations we’d like to have on route transitions. This is what my App function looks like in root.tsx (the main entry point for the Remix app):

import { AnimatePresence, motion } from 'framer-motion';
import { useOutlet, useLocation } from 'remix';

export default function App() {
  const outlet = useOutlet();
  const data = useLoaderData<LoaderData>();

  return (
    <ThemeProvider specifiedTheme={data.theme}>
      <Document>
        <Scripts />
        <Layout>
          <AnimatePresence exitBeforeEnter initial={false}>
            <motion.main
              key={useLocation().pathname}
              initial={{ x: '-10%', opacity: 0 }}
              animate={{ x: '0', opacity: 1 }}
              exit={{ y: '-10%', opacity: 0 }}
              transition={{ duration: 0.3 }}
            >
              {outlet}
            </motion.main>
          </AnimatePresence>
        </Layout>
      </Document>
    </ThemeProvider>
  );
}

As you can see, I added the exitBeforeEnter prop on <AnimatePresence>, because I want it to only render one component at a time. The exiting component will finished its exit animation before the entering component is rendered. Because I also want the initial load to not trigger an animation, I used the initial={false} prop. This will cause components present when AnimatePresence first loads to start in their animate state. Only components that enter after this initial render will animate in.

By keeping my <Layout> outside of the <AnimatePresence>, my header and footer will not be animated, only the content within the page, just what I wanted!

On <motion.main> you have to pass a key for Framer to be able to identify unique routes, I chose to pass the pathname provided by the built in useLocation() hook from Remix, which works fine. What remain is passing the exit, initial, animate and transition props, which kind of speak for themselves, and are well documented in the docs.

It’s a really basic animation, but I do like the outcome, it makes the site a bit more dynamic :-). Have a look at the live site at thomasledoux.be. Code can be found on Github.

Did you like this post? Check out my latest blog posts:

Maven on EC2
28 Jul 2023

Installing Maven on Ec2 Instance

  • aws
  • maven
  • cloud
Complete CICD Pipeline Java with Jenkins, Nexus, Sonar and AWS services
27 Jul 2023

From Code to Deployment - A Complete CICD journey for Java Apps using Jenkins, Nexus, Sonarqube, AWS ECR & ECS

  • devops
  • aws
  • cicd
Person typing on a laptop
21 Jul 2023

🏗️How to Make Animated ✨GIFs✨ For Amazon Web Services (AWS) Architectures:🏗️ A Step-by-Step Tutorial🏗️

  • aws
  • powerpoint
  • infra
  • resume