Animating Remix Pages with Motion
Remix (React router v7) is a powerful web framework that allows for fast, dynamic routing. When combined with Motion, you can create smooth, animated transitions between pages (Routes), enhancing the user experience. This article will guide you through the process of adding page transition animations to your Remix application using Motion.
Prerequisites
Before we begin, make sure you have:
-
A basic understanding of React and Remix
-
Remix installed in your project
-
Framer Motion installed (
npm install framer-motion
oryarn add framer-motion
)
Setting Up Your Remix Project
Let’s start with a typical Remix project structure:
app/
├── routes/
│ ├── _index.tsx
│ └── about.tsx
└── root.tsx
Your root.tsx
file might look something like this:
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
export default function Root() {
return (
<html lang="en">
<head>
<Links />
<Meta />
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
Step 1: Create Motion Components
First, we’ll wrap our page content in Framer Motion components. Update your route files (_index.tsx
and about.tsx
) like this:
import { motion } from 'framer-motion';
const MotionPage = motion.div;
export default function Index() {
return (
<MotionPage>
<h1>/</h1>
<p>index page</p>
</MotionPage>
);
}
Do the same for the about.tsx
file.
Step 2: Set Up AnimatePresence
To enable enter/exit animations, we need to use Framer Motion’s AnimatePresence
component. Update your root.tsx
:
import {
Links,
Meta,
useOutlet,
useLocation,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
import { AnimatePresence } from "framer-motion";
export default function Root() {
const outlet = useOutlet();
const location = useLocation();
return (
<html lang="en">
<head>
<Links />
<Meta />
</head>
<body>
<AnimatePresence mode='wait' initial={false}>
<div key={location.pathname}>
{outlet}
</div>
</AnimatePresence>
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
Note: We use useOutlet()
instead of <Outlet />
to allow for exit animations.
i tried both and <Outlet/>
skip exit animation
Step 3: Define Animation Variants
Create a new file variants.ts
to define your animation variants:
export const pageVariants = {
initial: {
opacity: 0, y: 50, }, in: {
opacity: 1, y: 0, transition: {
duration: 0.3, ease: "easeOut", }, }, out: {
opacity: 0, y: -50, transition: {
duration: 0.3, ease: "easeIn", }, },};
Step 4: Apply Animations to Pages
Now, update your route components to use these variants:
import { motion } from 'framer-motion';
import { pageVariants } from '../variants';
export default function Index() {
return (
<motion.div
initial="initial"
animate="in"
exit="out"
variants={pageVariants}
>
<h1>/</h1>
<p>index page</p>
</motion.div>
);
}
Apply the same changes to about.tsx
.
Step 5: Staggering Children Animations (Optional)
For more complex pages, you might want to stagger the animation of child elements. Here’s how you can do that:
// In variants.ts export const containerVariants = {
hidden: {
opacity: 0, y: 50, transition: {
when: "afterChildren", staggerChildren: 0.1, }, }, visible: {
opacity: 1, y: 0, transition: {
when: "beforeChildren", staggerChildren: 0.1, }, },};export const itemVariants = {
hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0 },};
Then in your route component:
import { motion } from 'framer-motion';
import { containerVariants, itemVariants } from '../variants';
export default function About() {
return (
<motion.div
initial="hidden"
animate="visible"
exit="hidden"
variants={containerVariants}
>
<motion.h1 variants={itemVariants}>/about</motion.h1>
<motion.p variants={itemVariants}>About page content</motion.p>
</motion.div>
);
}
Conclusion
By following these steps, you’ve successfully added smooth page transitions to your Remix application using Framer Motion. This technique enhances the user experience by providing visual continuity between route changes.
Remember to experiment with different animation variants to find the style that best suits your application’s design and feel. Happy coding!