Blog>
Snippets

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.