Animating Layout Transitions in Next.js 14 with Framer Motion
Example of implementing smooth layout transitions when navigating between pages in a Next.js 14 app using Framer Motion for animation.
import { motion, AnimateSharedLayout } from 'framer-motion';
// Wrap your application with AnimateSharedLayout to enable shared layout animations
function MyApp({ Component, pageProps }) {
return (
<AnimateSharedLayout>
<Component {...pageProps} />
</AnimateSharedLayout>
);
}
export default MyApp;
This snippet should be placed in your custom '_app.js' file. It wraps the entire application with the AnimateSharedLayout component from Framer Motion which allows for shared layout animations across the whole app.
import { motion } from 'framer-motion';
export default function PageLayout({ children }) {
// Define your animation variants
const variants = {
hidden: { opacity: 0, x: -100, y: 0 },
enter: { opacity: 1, x: 0, y: 0 },
exit: { opacity: 0, x: 100, y: 0 }
};
return (
<motion.div
initial="hidden"
animate="enter"
exit="exit"
variants={variants}
transition={{ type: 'linear' }}
>
{children}
</motion.div>
);
}
Create a `PageLayout` component that wraps the content of each page. This component uses the `motion.div` from Framer Motion with predefined animation variants for 'hidden', 'enter', and 'exit' states, as well as a transition property.
import PageLayout from './PageLayout'; // Assume PageLayout is in the same directory
function HomePage() {
return (
<PageLayout>
<h1>Welcome to the homepage</h1>
{/* Rest of your homepage content */}
</PageLayout>
);
}
export default HomePage;
This snippet shows how to use the `PageLayout` component in a specific page, in this case `HomePage`. The page's content should be wrapped inside `PageLayout` to enable the defined animations during page transitions.
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { motion, useAnimation } from 'framer-motion';
function usePageTransition() {
const controls = useAnimation();
const router = useRouter();
useEffect(() => {
const handleStart = () => {
controls.start("exit");
};
const handleComplete = () => {
controls.start("enter");
};
router.events.on('routeChangeStart', handleStart);
router.events.on('routeChangeComplete', handleComplete);
return () => {
router.events.off('routeChangeStart', handleStart);
router.events.off('routeChangeComplete', handleComplete);
};
}, [controls, router.events]);
return controls;
}
The custom hook `usePageTransition` can be used to control the start of exit and enter animations programmatically using Next.js router events. By calling this hook and passing the returned controls to a `motion` component, you can sync your animations with page transitions.