npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@14islands/react-page-transitions

v1.3.1

Published

Framework agnostic page transitions for react

Downloads

91

Readme

@14islands/react-page-transitions

Version

A framework-agnostic page transition lib for react. Tested with CRA, Next.js, Gatsby.js

As a creative studio we often have to adapt to our clients. It would be great if we could keep the same mental model and lifecycle for page transitions, no matter the react framework we end up using.

:bulb: This library doesn’t apply any styles to the page container, you need to manage it by yourself with an animation library or CSS transitions/animations.

Depends on react-transition-group for mounting/unmounting logic.

Design goals & features

  • Should work with most common React frameworks
  • Doesn't lock you in to any specific animation library
  • Adds CSS classnames with transition status to the page containers. CSS animations are more performant than JS, especially while the browser is loading assets.
  • Provides a hook usePageTransition() with transition status that can be accessed by any component in the tree
  • Relies on React.Suspense for delaying transitions if a page takes time to load
  • Supports default page transitions on history nav
  • Supports contextual transitions triggered by links - able to pass data to current and next page
  • Support barba.js sync mode - i.e. having 2 pages mounted at the same time (overlapping)
  • Doesn't touch scroll position - you need to manage it yourself depending on your framework and transition timings.

Getting Started

Install npm package

yarn add @14islands/react-page-transitions

Wrap your router pages

// App.js
return (
  <PageTransitions pageName={location.pathname}>
    <Routes location={location}>
      <Route path="/" element={<PageHome />} />
      <Route path="/a" element={<PageA />} />
      <Route path="/b" element={<PageB />} />
    </Routes>
  </PageTransitions>

Add default transitions using CSS classes

.page-appear,
.page-enter {
  opacity: 0;
}
.page-enter-active,
.page-appear-active {
  opacity: 1;
  transition: opacity 1s;
}
.page-exit {
  opacity: 1;
}
.page-exit-active {
  opacity: 0;
  transition: opacity 1s;
}

Get transition state using hook

function MyComponent() {
  const { transitionState, from, to } = usePageTransition();

  const styles = useSpring({
    from: {
      x: transitionState === "entering" ? "-100%" : "0%",
    },
  });

  return <animated.div style={styles} />;
}

Lifecycle

Page Load timeline

The following sequence describes a page loading for the first time.

sequenceDiagram
    participant suspended
    participant appear
    participant appearing
    participant appeared
    Note over suspended, appeared: Page A
    Note right of suspended: .page-appear-suspended
    Note right of appear: .page-appear
    appear->>appearing: onEnter({ isAppearing: true })
    Note right of appearing: .page-appear-active
    appearing->>appeared: onEntering({ isAppearing: true, done })
    Note right of appeared: .page-appear-done
    appeared->>appeared: onEntered({ isAppearing: true })

The suspended state only happens if the page suspended while mounting. It continues to the appear state automatically when suspense has resolved. To prevent this you can define your own <Suspense> further down the tree, which will cause the page to animate in and show your fallback instead.

Page Transtion timeline (mode: out-in)

The following sequence describes a page transition from "Page A" to "PageB" using the default mode "out-in". The current page exits before the new page enters.

sequenceDiagram
    participant exit
    participant exiting
    participant suspended
    Note over exit,suspended: Page A
    participant enter
    participant entering
    participant entered
    Note right of exit: .page-exit
    exit->>exiting: onExit()
    Note right of exiting: .page-exit-active
    exiting->>suspended: onExiting({ done })
    Note over suspended,entered: Page B
    Note right of suspended: .page-enter-suspended
    Note right of enter: .page-enter
    enter->>entering: onEnter()
    Note right of entering: .page-enter-active
    entering->>entered: onEntering({ done })
    Note right of entered: .page-enter-done
    entered->>entered: onEntered()

The suspended state only happens if the entering page (Page B) suspended while mounting. It continues to the appear state automatically when suspense has resolved. To prevent this you can define your own <Suspense> further down the tree, which will cause the page to animate in and show your fallback instead.

API

<PageTransitions>

Wrap your routes with this component so it can manage the lifecycle of mounting and unmounting pages.

By default it uses the "out-in" strategy, which means the current page animates out and is removed from the DOM before the new page is mounted and animates in.

It listens to CSS transitionend and CSS animationend events on the page container by default to trigger the lifecycle. This can be turned off either globally using the properties, or overridden per page via the onExiting and onEntering callbacks on the usePageTransitions hook.

:warning: transitionend and animationend will bubble up from the page children. Whichever animation or transition finish first, will mark the page transition as done.

If you want to take manual control of the transition duration, you can use the callbacks onEntering and onExiting on the usePageTransition hook.

| Property | type |  default | | ------------------- | ------------------------ | --------- | | mode | out-in | in-out | sync | "out-in" | | className | string | "page" | | timeout | number | undefined | | detectAnimationEnd | boolean | true | | detectTransitionEnd | boolean | true |

// App.js
return (
  <PageTransitions pageName={location.pathname}>
    // Note: need to pass location to Routes
    <Routes location={location}>
      <Route path="/" element={<PageHome />} />
      <Route path="/a" element={<PageA />} />
      <Route path="/b" element={<PageB />} />
    </Routes>
  </PageTransitions>
// _app.js
function MyApp({ Component, pageProps, router }) {
  console.log("router.pathname", router.pathname);
  return (
    <PageTransitions pageName={router.pathname}>
      <Component {...pageProps} />
    </PageTransitions>
  );
}
// gatsby-browser.js
export const wrapPageElement = ({ element, props: { location } }) => {
  return (
    <PageTransitions pageName={location.pathname}>{element}</PageTransitions>
  );
};

usePageTransition()

This hook allows any component nested within <PageTransitions> to listen and react to the transition state. It can also accept callbacks that will fire during the transition lifecycle.

function MyPage() {
  const { transitionState, from, to, data } = usePageTransition({
      onEnter: ({ from, to }) => void,
      onEntering: ({ from, to, done, data }) => void
      onEntered: ({ from, to }) => void,
      onExit: ({ from, to }) => void,
      onExiting: ({ from, to, done, data }) => void
  })
}

Return values

| transitionState | description | | --------------- | ------------------------------------------- | | suspended | Page is waiting to mount | | appear | Page is about to animate in on page load | | appearing | Page is currently animating in on page load | | appeared | Page has finished animating in on page load | | exit | When page is about to animate out | | exiting | Page is currently animating out | | exited | Page has finished animating out | | enter | Page was mounted and is about to enter | | entering | Page is currently animating in | | entered | Page has finished animating in |

Callbacks

| callbacks | callback params |  description | | ---------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------- | | onEnter | { isAppearing, from, to, data } | Page was mounted and is about to enter | | onEntering | { isAppearing, from, to, done, data } | Page is currently animating in - the done function has to be called to indicated animation is complete | | onEntered | { isAppearing, from, to, data } | Page finished animating in after page navigation | | onExit | { from, to } | Page is about to exit due to page navigation | | onExiting | { from, to, done } | Page is currently animating out - the done function has to be called to indicated animation is complete |

setPageTransitionData

This function can be used to pass state to the pages exiting and entering. This enables contextual transitions for when clicking on specific links.

setPageTransitionData(data: any): void

The user is in control of figuring out what to do with that data.

function HomePage() {
    const { transitionState, from, to } = usePageTransition({
        onExiting: async ({ from, to, done, data }) => {
            // `data` is what was passed to setPageTransitionData()
            if (data === "home-list-item") {
                await animationOutContextual()
            } else {
                await animationOutDefault()
            }
            done()
        }
    })
    return (
        <div>
        { items.map( item =>
            <Link to={item.url} onClick={() => setPageTransitionData("home-list-item")}>
                {item.label}
            </Link>
        )}
        </div>
    )
}

function ItemPage() {
    const { transitionState, from, to } = usePageTransition({
        onEntering: async ({ from, to, done, data }) => {
            // `data` is what was passed to setPageTransitionData()
            if (data === "home-list-item") {
                await animationInContextual()
            } else {
                await animationInDefault()
            }
            done()
        }
    })
    return ...
}

FAQ

import { useLocation, useNavigationType } from "react-router-dom";

function ScrollToTop() {
  const location = useLocation();
  const action = useNavigationType();

  useEffect(
    function scrollToTopWhenNavForward() {
      if (action !== "POP") {
        window.scrollTo(0, 0);
      }
    },
    [location, action]
  );

  return null;
}