Components (for Next.js)
PageTransition

PageTransition

One-stop solution for implementing page transitions in Next.js.

Takes care of

  • keeping the outgoing page mounted until the out phase is complete
  • preventing Next.js from removing page styles of the outgoing page too soon
  • restoring scroll position
  • handling anchor links correctly

Cross-fade transitions (or any kind of transition where the out and in phases overlap) are not supported by this package.

⚠️

PageTransition has not been tested (and may not work at all) running in the app folder of a Next.js project.

Installation

npm install @madeinhaus/nextjs-page-transition

Basic Usage

_app.tsx
import type { AppProps } from 'next/app';
import PageTransition, { useAsPathWithoutHash } from '@madeinhaus/nextjs-page-transition';
import '@madeinhaus/nextjs-page-transition/dist/index.css';
 
const App = ({ Component, pageProps }: AppProps) => {
  const key = useAsPathWithoutHash();
  return (
    <PageTransition>
      <Component {...pageProps} key={key} />
    </PageTransition>
  );
};
 
export default App;

Props

All props are optional.

  • as React.ElementType (default: 'main')
    The element type to use for the container element.
  • inPhaseDuration number (default: 600)
    The duration of the in phase in milliseconds.
  • outPhaseDuration number (default: 600)
    The duration of the out phase in milliseconds.
  • disableDefaultStyles boolean (default: false)
    Disable the default styles.
  • className string (default: undefined)
    The class name to apply to the container element.

PageTransitionContext

Use PageTransitionContext if you need more control over transitions.

_app.tsx
import type { AppProps } from 'next/app';
import Header from 'components/Header';
import PageTransition, {
  PageTransitionContext,
  useAsPathWithoutHash,
} from '@madeinhaus/nextjs-page-transition';
import '@madeinhaus/nextjs-page-transition/dist/index.css';
 
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
  const key = useAsPathWithoutHash();
  return (
    <PageTransitionContext>
      <Header />
      <PageTransition>
        <Component {...pageProps} key={key} />
      </PageTransition>
    </PageTransitionContext>
  );
};
 
export default App;

PageTransitionContext goes together with the useTransitionState hook, which returns the current transition state.

components/Header.tsx
import { usePageTransitionState } from '@madeinhaus/nextjs-page-transition';
 
const Header = () => {
  const { phase } = usePageTransitionState();
  return (
    <header>
      <h1>Header</h1>
      <p>PageTransitionPhase.{phase}</p>
    </header>
  );
};
 
export default Header;

Link

A convenience component to disable scrolling to the top of the page after navigation. It behaves exactly the same as Next.js' Link component, with the exception that it sets scroll={false} by default.

useNextCssRemovalPrevention

Next.js removes the styles of the outgoing page as soon as the new page is mounted. Since PageTransition keeps the outgoing page mounted until the 'out' phase is complete, this could cause the page to break.

This hook prevents the removal of page styles on route change and during the 'out' phase of the transition.

Please note that PageTransition already takes care of calling this hook for you, so you only need to explicitly call it if you're not using PageTransition but other methods to keep the outgoing page mounted, such as Framer Motion's AnimatePresence, react-transition-group, etc.

This hook returns a function that you can call to remove unused styles when the 'out' phase of the transition is complete and the new page mounts. You don't need to do this (useNextCssRemovalPrevention will take care of it automatically), but it's a good idea to do it anyway to prevent CSS scope collisions. Unused styles are only removed at the next route change, so lingering styles from the outgoing page can potentially override styles of the incoming page (PageTransition takes care of this automatically, so you only need to worry about it if you're using your own page transition solution).

This hook is not necessary if your app does not utilize page stylesheets, but CSS-in-JS solutions like styled-jsx, styled-components, emotion, tailwind, etc.

useTransitionState

Returns the current transition state. Requires PageTransitionContext to be used in the component tree, as an ancestor of both PageTransition and the component using this hook.

interface PageTransitionState {
  phase: PageTransitionPhase;
  phaseOutAnticipated?: boolean;
  currentUrl?: string | null;
  targetUrl?: string | null;
  scrollPosY?: number;
}
 
enum PageTransitionPhase {
  IDLE = 'IDLE',
  APPEAR = 'APPEAR',
  IN = 'IN',
  OUT = 'OUT',
}

useAsPathWithoutHash

A convenience hook that returns the router's current asPath without the hash part, to be used as the key prop for the page component.

If asPath is used as the key prop, React will unmount the page component and mount a new one when the hash part of the URL changes. This is not desirable, as anchor links within a page should not trigger a page transition. This hook returns the asPath without the hash part, ensuring that the page component is not unmounted when the hash part changes.