shuding / next-view-transitions

Use CSS View Transitions API in Next.js App Router.
https://next-view-transitions.vercel.app
MIT License
1.12k stars 32 forks source link

Extend useRouter to also use transitions #18

Open davelsan opened 1 month ago

davelsan commented 1 month ago

Related to #17

This PR is a quick POC to check whether extending the Next.js useRouter hook to enable transitions would work.

So far I've tried it only on the example demo and a very quick project I'm developing where I want to use programmatic transitions.

Would it be interesting to develop this further?

Some things I'm thinking:

vercel[bot] commented 1 month ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
next-view-transitions-example ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 4, 2024 10:29pm
davelsan commented 4 weeks ago

Some additional thoughts after tinkering further with this.

davelsan commented 2 weeks ago

I thought I'd give more context on what this PR does by adding a clear example. Sometimes it's hard to see it in the code.

The changes so far are two:

  1. Allow programmatic navigation via the router object, instead of using the <Link /> component.
  2. Optionally pass a custom transition animation controlled by JS, for example via conditionals, instead of having them declared in the CSS.

The follow snippet provides a simple example of using programatic navigation.

import { useTransitionRouter } from 'next-view-transitions'
import { useState } from "react";

export default function Page() {
  const [withCustomTransition, setWithCustomTransition] = useState(true)
  const router = useTransitionRouter();

  const routerNavigate = () => {
    router.push('/path/to/page', {
        onTransitionReady: withCustomTransition ? slideInOut: undefined,
    });
  }

  return (
    // Markup with an onWhatever event that calls `routerNavigate`
  )
}

In the example, regular useState sets the flag to either use a custom slide animation (slideInOut), or use the default defined in CSS (undefined). The slideInOut function follows the MDN documentation for the View Transitions API and Web Animations API. It can be defined outside of the React component tree.

function slideInOut() {
    // Outgoing page slides out towards the left while fading out.
    document.documentElement.animate(
        [
            {
                opacity: 1,
                transform: 'translate(0, 0)',
            },
            {
                opacity: 0,
                transform: 'translate(-100%, 0)',
            },
        ],
        {
            duration: 500,
            easing: 'ease-in-out',
            fill: 'forwards',
            pseudoElement: '::view-transition-old(root)',
        }
    );

    // Incoming page slides from the right while fading in.
    document.documentElement.animate(
        [
            {
                opacity: 0,
                transform: 'translate(100%, 0)',
            },
            {
                opacity: 1,
                transform: 'translate(0, 0)',
            },
        ],
        {
            duration: 500,
            easing: 'ease-in-out',
            fill: 'forwards',
            pseudoElement: '::view-transition-new(root)',
        }
    );
}

That's pretty much it. This code is already in the repo example of this branch. Feedback very welcome.