Functional Bytes

NextJS - Animated Page Transitions

_app.tsx

import { CSSTransition, TransitionGroup } from 'react-transition-group';

function App<P>({ Component, pageProps }: AppProps<P>) {
  const router = useRouter();

  return (
    <TransitionGroup className="app_transition_group">
      <CSSTransition
        key={router.pathname}
        classNames={animationClass}
        timeout={700}
        onExiting={() => {
          const mainElements = document.getElementsByTagName('main');
          if (mainElements.length > 1) {
            mainElements[1].style.marginTop = `${-scrollOffset.current}px`;
          }
        }}
        onEntered={() => {
          window.scrollTo(0, 0);
        }}
      >
        <Component {...pageProps} />
      </CSSTransition>
    </TransitionGroup>
  );
}

Major Issue - CSS modules are unloaded too early and an unstyled version of the page being navigated away from flashes unstyled. This thread discusses the issue and possible workarounds: https://github.com/vercel/next.js/issues/17464

Workaround

usePageTransitionFix.ts

import { useRouter } from 'next/router';
import { useEffect, useRef } from 'react';

export const usePageTransitionFix = () => {
  const router = useRouter();
  const currentPathRef = useRef<string>(router.asPath);
  currentPathRef.current = router.asPath;

  useEffect(() => {
    // Disable server side style sheet removal labeled with data-n-p
    const serverStyleSheets = document.querySelectorAll('link[data-n-p]');
    serverStyleSheets.forEach((styleElement) =>
      styleElement.removeAttribute('data-n-p'),
    );

    // Disable styles removal done by setting media to 'x'
    const styleElements = document.querySelectorAll('style[media="x"]');
    styleElements.forEach((styleElement) =>
      styleElement.removeAttribute('media'),
    );
  }, [router.asPath]);
};

Another issue is scroll restoration - when navigating back, the page is scrolled to the top rather than back to where the user was last viewing:

https://github.com/vercel/next.js/issues/20951

Reference