Rainy_Frog11
Rainy_Frog11

Reputation: 45

Preserve Exit Animations with React Router v6 and Framer Motion

I'm currently working on a project that involves React Router and Framer Motion v6. I'm trying to preserve exit animations between routes, but I've run into a bit of a roadblock.

Does anyone have experience with this? Specifically, I'm trying to figure out how to achieve this using the new v6 syntax. I've tried using createRoutesFromElements and createBrowserRouter, but I'm unable to pass AnimatePresence as a parent to the routes or route components. While using the previous, old BrowserRouter works, I can't utilize the data loader with that.

Any pointers would be greatly appreciated.

function App() {
  const router = createBrowserRouter(
    createRoutesFromElements(
      <>
        <Route
          path="/"
          element={
            <AnimatePresence mode="wait">
              <PrivateRoutes>
                <ScrollToTop>
                  <Layout />
                </ScrollToTop>
              </PrivateRoutes>
            </AnimatePresence>
          }
        >
          <Route index element={<Dashboard />} />
          <Route
            path="/transactions"
            element={<Transactions />}
            loader={({ params }) => {
              console.log("PARAMS:", params);
              return {
                hello: "world",
              };
            }}
          />
          <Route path="/disputes" element={<Disputes />} />
          <Route path="/cards">
            <Route index element={<Cards />} />
            <Route path=":id" element={<CardsIdLayout />}>
              <Route path="details" element={<CardDetails />} />
              <Route path="transactions" element={<CardTransactions />} />
            </Route>
          </Route>
          <Route path="/users">
            <Route index element={<Users />} />
            <Route path=":id" element={<UsersDetailsLayout />}>
              <Route index element={<UserDetails />} />
              <Route
                path="transactions"
                element={<UsersTransactions />}
              />
              <Route path="cards" element={<UsersCards />} />
            </Route>
          </Route>
        </Route>
        <Route path="*" element={<NotFound />} />
        <Route path="/auth" element={<PublicRoutes />}>
          <Route path="login" element={<Login />} />
          <Route path="reset-password" element={<ResetPassword />} />
        </Route>
      </>
    )
  );

  return <RouterProvider router={router} />;
}

Upvotes: 0

Views: 1226

Answers (2)

Adik Babaev
Adik Babaev

Reputation: 1

It helped me. framer-motion: 11.11.10, react-router-dom: 6.6.1.

export const AnimatedOutlet: React.FC = () => {
  const outlet = useOutlet();

  const location = useLocation();
  const appKey = location.pathname.split('/')[1];

  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={appKey}
        variants={pageTransitionVariants}
        initial="initial"
        animate="animate"
        exit="exit"
      >
        {outlet}
      </motion.div>
    </AnimatePresence>
  );
};

Upvotes: 0

Adam Smedberg
Adam Smedberg

Reputation: 26

I have a similar setup and I was able to get exit animations to work while using a dynamic loader. I believe the trick is to use an animated outlet component.

const AnimatedOutlet = () => {
    const o = useOutlet();
    const [outlet] = useState(o);

    return <>{outlet}</>;
};

<AnimatedPresence mode="wait">
    <AnimatedOutlet key={location} />
</AnimatedPresence>

Then in your Transactions element, you may need to put the loader data in state so that the data isn't lost as soon as you navigate away breaking the exit animation.

const [data] = useState(useLoaderData())

Hope this helps.

Upvotes: 1

Related Questions