Implementing view transitions in a Next JS application using React

By this time, you might already know about View Transitions API. In short, they provide mechanism for animating transition between two DOM states. For example, if you want to smoothly transition from one page to another page, you can do so with view transitions API.

In the past, I had implemented page transitions using React Transition Group. It seemed little complex to understand, and it didn't work well with accessibility. Now that browsers provide an in-built API to do the page transitions, it's time to update.

There's a great example in Chrome for Developers page that shows how to implement the view transitions API. In this blog post, let me show you a simple implementation in my blog that uses Next.js.

Before View Transitions

As you can see in the below GIF image, when I navigate to different pages in my blog, the browser loads the new page without anu smooth animation.

page navigation without any transition

After View Transitions

Let's take a look at the same page navigation, but this time with view transitions added. (The animation is not smooth, because of GIF, but in browser it will be butter smooth.)

page navigation with view transitions

View Transition Implementation in Next.js application

I use Next.js to build my blog. Next.js provides a Link component to render hyperlinks and navigate to the pages. We can wrap this Link component in our custom LinkTransition component that enables the view transition.

// LinkTransition.jsx

import Link from "next/link";
import { useRouter } from "next/navigation";

function LinkTransition(props) {
  const router = useRouter();
  const handleClick = (e) => {
    if (!document.startViewTransition) {
      // browser does not support view transition. Continue the default behavior.
      return;
    } else {
      // browser supports view transition. Animate the transtion.
      e.preventDefault();
      document.startViewTransition(() => {
        router.push(props.href);
      });
    }
  };

  return (
    <Link onClick={handleClick} {...props}>
      {props.children}
    </Link>
  );
}
export default LinkTransition;

Once we have this component, you can use it in place of Link component to have the page navigation transitioned.

Customizing the transition animation

By default, the view transition API just fades in / fades out the DOM elements. But you can customize the animation via CSS, by applying animations to ::view-transition-old and ::view-transition-new pseudo-elements. For example, here is a sliding animation, taken from Chrome Web Developer page.

/* global.css */

@keyframes fade-in {
  from { opacity: 0; }
}

@keyframes fade-out {
  to { opacity: 0; }
}

@keyframes slide-from-right {
  from { transform: translateX(30px); }
}

@keyframes slide-to-left {
  to { transform: translateX(-30px); }
}

::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}

::view-transition-new(root) {
  animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

Conclusion

Implementing page transitions have never been this much easier - thanks to View Transition API. Not all the browser supports it yet, but until they do, we can progressively enhance the user experience in browsers that do support it (Chrome / Edge).