DEV Community

Cover image for Create awesome Page Transitions in NextJS using Framer motion
Shaan Alam
Shaan Alam

Posted on • Edited on

Create awesome Page Transitions in NextJS using Framer motion

I recently decided to re-design my portfolio in NextJs. So, I started researching around the web for some cool page transitions which I would like to have for my portfolio, and I came across something like this. (Similar to Barba.js)
Alt Text

So, I decided to figure out and create this transition. In this tutorial, I am going to explain the steps I took to create this nice page transition.
Let's start.

Note - In this example, I will only be talking about the CSS relevant to the animations. If you want the whole CSS as well, there is a github repo mentioned at the end of the tutorial.

New project, who-hoo!!

First, we need to create a new NextJS project by typing the below command into your terminal.

npx create-next-app nextjs-page-transition-example
Enter fullscreen mode Exit fullscreen mode

And now open your newly created project folder into your favourite Editor.

Type npm run dev in your terminal to start the dev server on PORT 3000.

Installing Framer motion

Type npm install framer-motion --save to install framer motion to your project.

A little setup

Now that we have installed framer motion, we need to set something up. Open your _app.js in the pages directory and import AnimatePresence from framer-motion and wrap it around the main content like this.

import "../styles/globals.css";
import { AnimatePresence } from "framer-motion";

function MyApp({ Component, pageProps, router }) {
  return (
    <AnimatePresence exitBeforeEnter>
      <Component {...pageProps} key={router.route} />
    </AnimatePresence>
  );
}

export default MyApp;
Enter fullscreen mode Exit fullscreen mode

AnimatePresence helps to animate React components when they are removed from React tree. It kind of helps for making exit animations for components. The exitBeforeEnter prop on AnimatePresence tells framer-motion to remove the current component with animation and then start the animation for new component.

Creating the pages

Now, we need to create different pages so that we can actually have transition animation between them. In this example, we are going to create just 3 simple pages. (Home, about, services).

Open index.js in the pages directory, and create a basic React component.

function Home() {
  return (
    <main>
      <h1>Home Page</h1>
    </main>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

Now, we need to create two more files about.js and services.js like so.

function Services() {
  return (
    <main>
      <h1>Services Page</h1>
    </main>
  );
}

export default Services;
Enter fullscreen mode Exit fullscreen mode
function Services() {
  return (
    <main>
      <h1>Services Page</h1>
    </main>
  );
}

export default Services;
Enter fullscreen mode Exit fullscreen mode

Creating the Navbar

Now that we have created three basic pages, we need navbar with links so that we could transition between pages.
Create a folder called components in the root directory and in that folder create a Navbar.js with the following content.

import Link from "next/link";

export default function Navbar() {
  return (
    <nav className="navbar">
      <div className="container">
        <Link href="/">Home</Link>
        <Link href="/about">About</Link>
        <Link href="/services">Services</Link>
      </div>
    </nav>
  );
}
Enter fullscreen mode Exit fullscreen mode

This is a basic navbar which will help to navigate between the pages. The Link is a built in nextjs module which will create a link in the DOM that will navigate to other pages.

Now, we need to import the Navbar into the pages we've created! So the final files should be looking something like these.

function Home() {
  return (
    <main>
      <h1>Home Page</h1>
    </main>
  );
}
export default Home;
Enter fullscreen mode Exit fullscreen mode
function About() {
  return (
    <main>
      <h1>About Page</h1>
    </main>
  );
}

export default About;

Enter fullscreen mode Exit fullscreen mode
function Services() {
  return (
    <main>
      <h1>Services Page</h1>
    </main>
  );
}

export default Services;
Enter fullscreen mode Exit fullscreen mode

Animating pages - The fun Part

Let's understand the animation first. This animation is divided into two parts. The sliding-in animation on current page and sliding out on the next page. Take a look at the figure below to understand better.
Sliding in animation on page 1
Sliding in
Sliding out animation on page 2
Sliding out

We need to animate two divs (one for sliding in and the other for sliding out animation) for every page we want to have animation for. We can create a component for these two divs, but adding these two divs to every page can be hectic, so we are going to create a Higer Order Component for this purpose.

HOC

Create a folder called HOC and in that folder, create a file called withTransition.js. In this file, we are going to import motion from framer motion and also create a function that is going to return a new component with the animating divs.

import { motion } from "framer-motion";

const withTransition = (OriginalComponent) => {
  return () => (
    <>
      <OriginalComponent />
      <motion.div
        className="slide-in"
        initial={{ scaleX: 0 }}
        animate={{ scaleX: 0 }}
        exit={{ scaleX: 1 }}
        transition={{ duration: 1, ease: "easeInOut" }}
      />
      <motion.div
        className="slide-out"
        initial={{ scaleX: 1 }}
        animate={{ scaleX: 0 }}
        exit={{ scaleX: 0 }}
        transition={{ duration: 1, ease: "easeInOut" }}
      />
    </>
  );
};

export default withTransition;
Enter fullscreen mode Exit fullscreen mode
  .slide-in {
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    width: 100%;
    background: #066bb8;
    transform-origin: left;
  }

  .slide-out {
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    width: 100%;
    background: #066bb8;
    transform-origin: right;
  }
Enter fullscreen mode Exit fullscreen mode

Now, we need to include this HOC in every page we want to have page transition for. For example, I want to have animations for all 3 of my pages so, I'd import withTransition in every page and pass the component within it like so.

import Navbar from "../components/Navbar";
import withTransition from "../HOC/withTransition";

function Home() {
  return (
    <>
      <Navbar />
      <main>
        <h1>Home Page</h1>
      </main>
    </>
  );
}

export default withTransition(Home);
Enter fullscreen mode Exit fullscreen mode
import Navbar from "../components/Navbar";
import withTransition from "../HOC/withTransition";

function About() {
  return (
    <>
      <Navbar />
      <main>
        <h1>About Page</h1>
      </main>
    </>
  );
}

export default withTransition(About);
Enter fullscreen mode Exit fullscreen mode
import Navbar from "../components/Navbar";
import withTransition from "../HOC/withTransition";

function Services() {
  return (
    <>
      <Navbar />
      <main>
        <h1>Services Page</h1>
      </main>
    </>
  );
}

export default withTransition(Services);
Enter fullscreen mode Exit fullscreen mode

And that's it....🎊️

We have now completed the page transition. The demo and GitHub links are provided below
Demo - https://nextjs-transition-example.vercel.app/
GitHub repo - https://github.com/shaan71845/nextjs-page-transition-example

Feel free to leave a 🌠️
Thanks for reading!!

Top comments (6)

Collapse
 
teh_builder profile image
Daniel Schmidt

Be aware that this doesn't work with the Next.js RouteAnnouncer (for a11y). It looks for the first h1 after the route changes, and announces it as a the new title - This will be the previous page page title, since the new page isn't animated in.

Collapse
 
mohdahmad1 profile image
Mohd Ahmad

how to animate image next js provide Image component that can not be used as motion.img

Collapse
 
javusscriptus profile image
Michal Zalobny

How to prevent website browser from "jumping" while changing the route? It happens for example when you switch the subpage while it is already scrolled a little, and new subpage is not scrolled at all

Collapse
 
shaancodes profile image
Shaan Alam

Try scroll={false} on the tag

Collapse
 
javusscriptus profile image
Michal Zalobny

Your idea is working, but it still jumps while using native controls (like the browser arrows)

Thread Thread
 
defite profile image
Nikita Makhov

Have you tried overflow: hidden on the parent container?