Headless UI "leave" transition not working in React

Viewed 4790

My navbar is setup such that on state change the hamburger menu opens and closes. While the enter animation works perfectly, the leave doesn't. My animation is a smooth slide in and slide out, but only the slide in works whereas on leave it just closes normally.

const NavbarMenu = ({ isOpen, menuClick }) => {
  return (
    <Transition appear={true} show={isOpen}>
      <Transition.Child
        class="flex flex-col bg-yellow-700 fixed top-0 right-0 p-5 z-20 w-1/2 h-full transition ease-in-out duration-300"
        enter="transition-opacity ease-in-out duration-700"
        enterFrom="translate-x-full"
        enterTo="translate-x-0"
        leave="transition-opacity ease-out duration-700"
        leaveFrom="opacity-100"
        leaveTo="opacity-0"
      >
        <Exit className="text-yellow-100 w-1/6" onClick={() => menuClick()} />
        <div className="flex flex-col gap-y-4 mt-10 font-poppins font-bold text-xl text-yellow-100">
          <span>About</span>
          <span>About</span>
          <span>About</span>
        </div>
      </Transition.Child>
    </Transition>
  );
};
3 Answers

Your leave transition is missing the translate-x classes.

You probably also want to add the opacity transition to the enter animation.

This should work:

const NavbarMenu = ({ isOpen, menuClick }) => {
  return (
    <Transition appear={true} show={isOpen}>
      <Transition.Child
        class="flex flex-col bg-yellow-700 fixed top-0 right-0 p-5 z-20 w-1/2 h-full transition duration-700"
        enter="ease-in-out"
        enterFrom="translate-x-full opacity-0"
        enterTo="translate-x-0 opacity-100"
        leave="ease-out"
        leaveFrom="translate-x-0 opacity-100"
        leaveTo="translate-x-full opacity-0"
      >
        <Exit className="text-yellow-100 w-1/6" onClick={() => menuClick()} />
        <div className="flex flex-col gap-y-4 mt-10 font-poppins font-bold text-xl text-yellow-100">
          <span>About</span>
          <span>About</span>
          <span>About</span>
        </div>
      </Transition.Child>
    </Transition>
  );
};

Most often when "leave transitions" doesn't work it's because it unmounts before the transition ends. To work around that you need a "between state" that is triggered when the Transition is in show mode. Then create a useEffect that listens to this state, and set a setTimeout that does the actual unmount of the element.

I found a solution, just delay the execution of set state to 0ms so the transition can trigger the leave property. I don't have a logical explanation but this worked smoothly.

    const NavbarMenu = ({ isOpen, menuClick }) => {
  return (
    <Transition appear={true} show={isOpen}>
      <Transition.Child
        class="flex flex-col bg-yellow-700 fixed top-0 right-0 p-5 z-20 w-1/2 h-full transition ease-in-out duration-300"
        enter="transition-opacity ease-in-out duration-700"
        enterFrom="translate-x-full"
        enterTo="translate-x-0"
        leave="transition-opacity ease-out duration-700"
        leaveFrom="opacity-100"
        leaveTo="opacity-0"
      >
        <Exit className="text-yellow-100 w-1/6" 
              onClick={() => 
                setTimeout(() => {
                    menuClick();
                }, 0);
              } />
        <div className="flex flex-col gap-y-4 mt-10 font-poppins font-bold text-xl text-yellow-100">
          <span>About</span>
          <span>About</span>
          <span>About</span>
        </div>
      </Transition.Child>
    </Transition>
  );
};
Related