Troubles animating react-router with framer-motion

Viewed 1392

I'm trying to add some animations and smoothness to an app with Framer-motion and I'm struggling to make it all work.

Using react-router 6, I want to trigger some exit animations on route sub-components when the url changes. Following this tutorial, here is what I got for the main layout :

export default function MainWrapper() {
  const location = useLocation();
  return (
    <Main>
      <AnimatePresence exitBeforeEnter initial={false}>
        <Routes key={location.pathname}>
          <Route path="/" element={<Dashboard />} />
          <Route path="project/:id/*" element={<Project />} />
        </Routes>
      </AnimatePresence>
    </Main>
  );
}

The pages Dashboard and Project are build using some composition of Antd's Row and Col system. I want to animate Row's children to appear one after the other on mount, and to disappear one after the other on unmount :

import React, { ReactNode } from "react";
import { Card, Col, Row } from "antd";
import { motion } from "framer-motion";

// Section

type SectionProps = {
  title?: ReactNode;
  extra?: ReactNode;
  children?: ReactNode;
  span?: number;
};

export default function Section({
  title,
  extra,
  children,
  span = 24
}: SectionProps) {
  return (
    <MotionCol span={span} variants={colVariant}>
      <Card title={title} extra={extra}>
        {children}
      </Card>
    </MotionCol>
  );
}

// Section.Group

type GroupProps = {
  children?: ReactNode;
};

Section.Group = function({ children }: GroupProps) {
  return (
    <MotionRow
      gutter={[24, 24]}
      variants={rowVariant}
      initial="hidden"
      animate="show"
      exit="close"
    >
      {children}
    </MotionRow>
  );
};

// Framer stuff

const MotionRow = motion(Row);
const MotionCol = motion(Col);

const transition = { duration: 0.4, ease: [0.43, 0.13, 0.23, 0.96] };

const rowVariant = {
  hidden: {},
  show: {
    transition: {
      staggerChildren: 0.1
    }
  },
  close: {}
};

const colVariant = {
  hidden: { opacity: 0, x: 20, transition },
  show: { opacity: 1, x: 0, transition },
  close: {
    opacity: 0,
    x: -20,
    transition
  }
};

Dashboard is then built using these blocks :

<Section.Group>
  <Section>
    First section...
  </Section>
  <Section>
    Second section...
  </Section>
</Section.Group>

The issue : Only hidden and show work. Not close. There is no exit-animation when leaving a page. How could I solve this ? Thank you.

2 Answers

Things I found wrong:

  1. For custom components the motion function requires you to forward the ref

Docs: https://www.framer.com/api/motion/component/#custom-components

const ForwardedAntdRow = React.forwardRef((props, ref) => (
  <Row ref={ref} {...props} />
));
const MotionRow = motion(ForwardedAntdRow);

not

const MotionRow = motion(Row);
  1. Route doesn't have an element prop
  <Route path="/page1">
    <Page1 />
  </Route>

is pretty standard notation as far as I know (I don't work with react-router often)

I created a working example here: https://codesandbox.io/s/framer-motion-animate-react-router-transition-kczeg?file=/src/index.js:1308-1372

I can answer any other questions you have when I am online tomorrow. Let me know

I can see two potential problems with your code:

1.

Note: Child motion components must each have a unique key prop so AnimatePresence can track their presence in the tree.

Note: The custom component being removed from the DOM must still be a direct descendant of AnimatePresence for the exit animation(s) it contains to trigger.

source: https://www.framer.com/api/motion/animate-presence/

So your code would become:

export default function MainWrapper() {
  const location = useLocation();
  return (
    <Main>
      
      <Routes key={location.pathname}>
        <AnimatePresence exitBeforeEnter initial={false}>
          <Route key="dashboard" path="/" element={<Dashboard />} />
          <Route key="project" path="project/:id/*" element={<Project />} />
        </AnimatePresence>
      </Routes>

    </Main>
  );
}
Related