htmlreactjsreact-router-domframer-motion

Transition between routes in react-router-dom v6.3


So I´m currently refactoring a website, and so I do with the rrd, which was on v5 in the previous website version. Now, that the component doesn´t exist anymore we have to work with the new component as you probably know.

I previously used framer-motion to transition in and out between the routes like so:

<Switch location={location} key={location.pathname}>
  <motion.div
    initial="initial"
    animate="in"
    exit="out"
    variants={pageVariants}
    transition={pageTransition}>
    <Route path="/audit" component={Audit} />
    <Route exact path="/smartx" component={SmartX} />
    <Route path="/smartx/erc20" component={TokenGenerator} />
    <Route path="/createAudit" component={PaymentStatus} />
    <Route path="/faq" component={FAQ} />
    <Route path="/support" component={Support} />
    <Route path="/terms" component={Terms} />
    <Route path="/policy" component={Policy} />
    <Route path="/about" component={About} />
    <Route exact path="/">
      <Redirect to="/audit" />
    </Route>
  </motion.div>
</Switch>;

Simply replacing the Switch component with the Routes component won´t work, since you can only have Route components as childs from . Moving the <motion.div> one layer up over the Routes component leads to only one initial fade in transition on page load.

new (not quiet working version):

<AnimatePresence>
  <PageLayout>
    <motion.div
      initial="initial"
      animate="in"
      variants={pageVariants}
      transition={pageTransition}>
      <Routes>
        <Route path="/audit" element={<Audit />} />
        <Route path="/smartx" element={<SmartX />} />
        <Route path="/faq" element={<FAQ />} />
        <Route path="/support" element={<Support />} />
        <Route path="/terms" element={<Terms />} />
        <Route path="/policy" element={<Policy />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </motion.div>
  </PageLayout>
</AnimatePresence>; 

Framer motion animations (equalin both old and new version):

  const pageVariants = {
    initial: {
      opacity: 0
    },
    in: {
      opacity: 1
    },
    out: {
      opacity: 0
    }
  };

  const pageTransition = {
    type: 'tween',
    ease: 'linear',
    duration: 0.5
  }; 

Any ideas on how to achieve a transition on each route switch?


Solution

  • I think your solution is close to working. Move the PageLayout component and motion.div into a layout route that uses the current path as a React key.

    Example:

    import { Outlet } from 'react-router-dom';
    
    const AnimationLayout = () => {
      const { pathname } = useLocation();
      return (
        <PageLayout>
          <motion.div
            key={pathname}
            initial="initial"
            animate="in"
            variants={pageVariants}
            transition={pageTransition}
          >
            <Outlet />
          </motion.div>
        </PageLayout>
      );
    };
    

    ...

    <Routes>
      <Route element={<AnimationLayout />}>
        <Route path="/audit" element={<Audit />} />
        <Route path="/smartx" element={<SmartX />} />
        <Route path="/faq" element={<FAQ />} />
        <Route path="/support" element={<Support />} />
        <Route path="/terms" element={<Terms />} />
        <Route path="/policy" element={<Policy />} />
        <Route path="/about" element={<About />} />
        <Route path="*" element={<Navigate to="/audit" replace />} />
      </Route>
    </Routes>