Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remix-Run Page Transition Animation With Framer Motion

I would like to add some page transitions to my web app. The problem is that the exit animation doesn't work.

I have a Motion Component

// app/components/Motion.tsx    
import React from "react";
import { motion } from "framer-motion";
export default function Motion(props: {
  children: React.ReactChild | React.ReactFragment | React.ReactPortal;
}) {
  return (
    <motion.div
      className="w-full h-screen bg-blue-400 flex pt-24 mx-auto"
      initial={{ opacity: 0, x: -100 }}
      animate={{ opacity: 1, x: 0 }}
      exit={{ opacity: 0, x: -100 }}
      transition={{ duration: 2 }}
    >
      {props.children}
    </motion.div>
  );
}

In which I want to wrap every page like this:

// app/routes/contact.tsx
import Motion from "~/components/Motion";

export default function contact() {
  return (
    <Motion>
      <div>Contact</div>
    </Motion>
  );
}

I know that I should use the <AnimatePresence> from framer-motion but I don't know where.

like image 748
EddieIO Avatar asked Oct 16 '25 22:10

EddieIO


2 Answers

Funny enough, I was just trying to figure out how to do this a few hours ago. I'm super new to Remix, but here's a really simple example of what I did to get route animations working.

  1. root.jsx
import {
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from 'remix'

import { AnimatePresence } from 'framer-motion'

import styles from './styles/app.css'

export function meta() {
  return {
    charset: 'utf-8',
    title: 'New Remix App',
    viewport: 'width=device-width,initial-scale=1',
  }
}

export function links() {
  return [{ rel: 'stylesheet', href: styles }]
}

export default function App() {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        <AnimatePresence exitBeforeEnter>
          <Outlet />
        </AnimatePresence>
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  )
}
  1. Example of first route index.jsx
import { motion } from 'framer-motion'

export default function Index() {
  return (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.5 }}
          className="w-full h-full md:col-span-3 sm:overflow-auto relative z-0"
        >

               {/* page content */}

        </motion.div>
  1. Same <motion.div></motion.div> parent elements as above to be applied to the next route (e.g. about.jsx) - including the same initial, animate, exit, and transition attributes.
like image 74
basil Avatar answered Oct 18 '25 10:10

basil


You can try this out at the “root.tsx” file.

     <AnimatePresence mode="wait">
      <motion.div
        key={useLocation().pathname}
        variants={{
          initial: { opacity: 0, y: -1000 },
          animate: { opacity: 1, y: 0 },
          exit: { opacity: 1, y: 1000 },
        }}
        initial="initial"
        animate="animate"
        exit="exit"
      >
        <Outlet />
      </motion.div>

Please note the “mode” prop instead of exitBeforeEnter (deprecated).

And also notice the key prop in the child.

Hope this helps. 😇

Source: fixtergeek.com

like image 37
Héctor BlisS Avatar answered Oct 18 '25 10:10

Héctor BlisS