Page Transition Effect in NextJS

anxiny

Anxiny

Posted on April 20, 2021

Page Transition Effect in NextJS

This method may not be the best way for NextJS v12+ since new API is provided.

Before we start building any component for the transition, let's briefly talk about how NextJS renders pages.

First, let's take a look at _app.js:

export default function MyApp({ Component, pageProps }) {
  return (
      <Component {...pageProps} />
  );
}

Enter fullscreen mode Exit fullscreen mode

The "_app.js" is the entry point for NextJS to start render page. When you navigation to a different page, the page component pass to MyApp as Component.

Therefore, in order to make a transition effect, we need to prevent

NextJS from rendering the new page before transition effect is done.

Now, let's create the layout component with some navigation links:


export default function TransitionLayout({ children }) {
  return (
    <div>
      <nav>
        <Link href="/">Home</Link>
        <Link href="/about">About</Link>
      </nav>
      <div>
        {children}
      </div>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

And add to _app.js

function MyApp({ Component, pageProps }) {
  return (
    <TransitionLayout>
      <Component {...pageProps} />
    </TransitionLayout>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, let's start working on the TransitionLayout

First, we need to prevent the rendering the new page

We can add a state to hold the current children, and only render the displayChildren.

We use children as the default value for displayChildren.


export default function TransitionLayout({ children }) {
  const [displayChildren, setDisplayChildren] = useState(children);
  return (
    <div>
      ...
      <div>
        {displayChildren}
      </div>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Now, if you click the link, the content of the page will not change.

Next, we add css and transition stage

.content {
  opacity: 0;
  background-color: cornflowerblue;
  transition: 1s;
}

.fadeIn {
  opacity: 1;
}

Enter fullscreen mode Exit fullscreen mode
export default function TransitionLayout({ children }) {
  const [displayChildren, setDisplayChildren] = useState(children);
  const [transitionStage, setTransitionStage] = useState("fadeOut");
  ...
  return (
    <div>
      ...
      <div
        className={`${styles.content} ${styles[transitionStage]}`}
      >
        {displayChildren}
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, the component will by default at 'fadeOut' stage, and we want to let it enter the 'fadeIn' stage at first time render, so let's add:

  useEffect(() => {
    setTransitionStage("fadeIn");
  }, []);

Enter fullscreen mode Exit fullscreen mode

Next, we want the component to enter 'fadeOut' when new children is received.

  useEffect(() => {
    if (children !== displayChildren) setTransitionStage("fadeOut");
  }, [children, setDisplayChildren, displayChildren]);

Enter fullscreen mode Exit fullscreen mode

And, render new children when 'fadeOut'is done, then re-enter 'fadeIn' stage.

  ...
  return(
      ...
      <div
        onTransitionEnd={() => {
          if (transitionStage === "fadeOut") {
            console.log("fading out");
            setDisplayChildren(children);
            setTransitionStage("fadeIn");
          }
        }}
        className={`${styles.content} ${styles[transitionStage]}`}
      >
        {displayChildren}
      </div>
  )
Enter fullscreen mode Exit fullscreen mode

And, here is the demo and the completed code for the layout component:
Node: The demo will take sometime for CodeSandbox to start.


import Link from "next/link";
import { useState, memo, useEffect } from "react";
import styles from "./Layout.module.css";

export default function TransitionLayout({ children }) {
  const [displayChildren, setDisplayChildren] = useState(children);
  const [transitionStage, setTransitionStage] = useState("fadeOut");
  useEffect(() => {
    setTransitionStage("fadeIn");
  }, []);

  useEffect(() => {
    if (children !== displayChildren) setTransitionStage("fadeOut");
  }, [children, setDisplayChildren, displayChildren]);

  return (
    <div>
      <nav>
        <Link href="/">Home</Link>
        <Link href="/about">About</Link>
      </nav>
      <div
        onTransitionEnd={() => {
          if (transitionStage === "fadeOut") {
            console.log("fading out");
            setDisplayChildren(children);
            setTransitionStage("fadeIn");
          }
        }}
        className={`${styles.content} ${styles[transitionStage]}`}
      >
        {displayChildren}
      </div>
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

Thank you all!!

💖 💪 🙅 🚩
anxiny
Anxiny

Posted on April 20, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Page Transition Effect in NextJS
nextjs Page Transition Effect in NextJS

April 20, 2021