nuxt: Nuxt3 - Transition appear Flag not working

Environment

  • Operating System: Linux
  • Node Version: v16.17.0
  • Nuxt Version: 3.0.0-rc.8
  • Package Manager: npm@8.15.0
  • Builder: vite
  • User Config: modules
  • Runtime Modules: @nuxtjs/tailwindcss@5.3.2
  • Build Modules: -

Reproduction

Repo https://github.com/rhernandog/nuxt3-transitions-issue

Live Sample https://stackblitz.com/edit/nuxt-starter-d2z5mn

Describe the bug

As mentioned in discussion #5717 there is an issue with the appear flag in the using transition, transition group and page transition. In the live sample you’ll see that the first load the page doesn’t appear. If you navigate you’ll see the fade out/in transition between pages working as expected. In the users page you should see an animation for a list of items that never happens, but if you make a change in the file and save it, the animation will happen as expected after the hot reload.

As reported in the discussion I can confirm that all these animations work as expected in a Vue3 project.

Additional context

No response

Logs

No response

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Comments: 21 (9 by maintainers)

Most upvoted comments

@danielroe Thanks for looking into this! 👍

I’ll create an issue in Vue and link it here so people can track it: https://github.com/vuejs/core/issues/6951

This appears to be reproducible in a non-Nuxt setup and is probably an issue with the vue <Transition> component + SSR. Would you open an issue in https://github.com/vuejs/core?

https://stackblitz.com/edit/github-ncyrhd

It’s probably because the transition is inside the suspense but it needs to be outside? Not sure. combining-with-other-components

For anyone visiting this issue before it gets fixed: this is a ‘hacky’ workaround that works for me:

Make sure you set the ‘from’ state in your CSS to not have a delay:

// plugins/initial_load.ts
const { toggleTransitionComplete } = useTransitionComposable();

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('app:mounted', () => {
    const tl = useGsap.timeline({
      paused: true,
      onComplete: () => {
        toggleTransitionComplete(true)
      }
    });

    tl.to(".loading-screen", {
        duration: 2,
        top: window.innerHeight,
        ease: "expo.inOut",
    });

    tl.play();
  })
});
.loading-screen {
  position: relative;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  width: 100%;
  height: calc(100% + 300px);
  display: flex;
  flex-direction: column;
  // top: 100%; <-- this was before (when trying to use it with hooks) and is the state when the page is loaded (to reveal the page)
  top: -300px; // <-- changed to this and is the state in between pages

// [...]
}

This is an upstream issue in vue itself, as linked above. Please check that issue for updates.

I just needed to wrap my <Transition name="fade" appear> with <ClientOnly> and now the animation is working for me.

In fact it does not seem to be resolved in Vue either, using the reproduction linked in https://github.com/vuejs/core/issues/6951.

Would you be willing to investigate and open a new issue in https://github.com/vuejs/core?

Unfortunately, after upgrading Nuxt to 3.8.1 (which uses vue 3.3.8) it still seems to be an issue as I don’t get a transition on the initial load using hooks or classes (following the GSAP example on Stackblitz):

Code snippet of transition configuration that is used in definePageMeta() on all pages:

import { useTransitionComposable } from "../composables/useTransitionComposable";

const { toggleTransitionComplete } = useTransitionComposable();

export const pageTransitionConfig = {
  // name: 'general-transition',
  mode: 'out-in' as any,
  appear: true,
  onAppear(el: Element) {
    console.log('on appear')
    useGsap.set('.loading-screen', { top: -300 });
  },
  onBeforeEnter(el: Element) {
    console.log('on before enter');
    console.log(el);
    useGsap.set('.loading-screen', { top: -300 });
  },
  onEnter: (el: Element, done: () => void) => {
    const tl = useGsap.timeline({
      paused: true,
      onComplete: () => {
        toggleTransitionComplete(true)
        done()
      }
    });

    tl.to(".loading-screen", {
        duration: 0.65,
        top: window.innerHeight,
        ease: "expo.inOut",
        delay: 0.1,
    });

    tl.play();
  },
  onEnterCancelled(el: Element) {
    useGsap.set('loading-screen', { top: window.innerHeight });
  },
  onLeave(el: Element, done: () => void) {
    toggleTransitionComplete(false);

    const tl = useGsap.timeline({
      paused: true,
      onComplete: done 
    })

    tl.to(".loading-screen", {
      duration: 0.45,
      top: -300,
      ease: "power2.out",
    });

    tl.play();
  }
}

export default pageTransitionConfig;

Neither on appear nor on before enter appears in the console. Also, the position is not set. However, it does run on navigating between routes. Furthermore, I tried to get it to work by using hooks in nuxt.config.ts and adding the config to the transition property on the NuxtPage component. Unfortunately, both don’t seem to show a difference; still no initial animation or console log.

Maybe as it’s resolved in vue it now has something to do with Nuxt?

When hot-module reload, reloads the page it does log the console from different parts: image