Search code examples
typescriptnext.jstailwind-cssframer-motion

How to use AnimatePresence for clean entry and exit?


I am trying to make a clean entry/exit animation in my next app using Framer Motion's AnimatePresence component. I have tried using the delay component but only works for one component. For the other components, they just vanish without any animation. The expected behavior and current behavior is shown below:

Expected & Working behavior

Current & Not Working Behavior

The code of the components is below:

<AnimatePresence>
                  {isAboutVisible && (
                    <motion.div
                      initial={{ x: 1050, opacity: 0 }}
                      animate={{ x: 0, opacity: 1 }}
                      transition={{
                        duration: 1.5,
                        ...(isSkillsVisible || isProjectsVisible
                          ? { delay: 1.5 }
                          : {}),
                      }}
                      exit={{ x: 1050, opacity: 0 }}
                    >
                      <About />
                    </motion.div>
                  )}
                </AnimatePresence>
                <AnimatePresence>
                  {isSkillsVisible && (
                    <motion.div
                      initial={{ x: 1050, opacity: 0 }}
                      animate={{ x: 0, opacity: 1 }}
                      transition={{
                        duration: 1.5,
                        delay: 1.5,
                      }}
                      exit={{ x: 1050, opacity: 0 }}
                    >
                      <Skills />
                    </motion.div>
                  )}
                </AnimatePresence>
                <AnimatePresence>
                  {isProjectsVisible && (
                    <motion.div
                      initial={{ x: 1050, opacity: 0 }}
                      animate={{ x: 0, opacity: 1 }}
                      transition={{ duration: 1.5, delay: 1.5 }}
                      exit={{ x: 1050, opacity: 0 }}
                    >
                      <Projects />
                    </motion.div>
                  )}
                </AnimatePresence>

Solution

  • I think all conditions must be in one AnimatePresence and mode="wait" prop must be added to it

    sth like this :

     <AnimatePresence  mode="wait" initial={false}>
              {isAboutVisible && (
                <motion.div
                  key="about"
                  initial={{ x: 1050, opacity: 0 }}
                  animate={{ x: 0, opacity: 1 }}
                  transition={{
                    duration: 1.5,
                    ...(isSkillsVisible || isProjectsVisible ? { delay: 1.5 } : {}),
                  }}
                  exit={{ x: 1050, opacity: 0 }}
                >
                  <About />
                </motion.div>
              )}
              {isSkillsVisible && (
                <motion.div
                  key="skills"
                  initial={{ x: 1050, opacity: 0 }}
                  animate={{ x: 0, opacity: 1 }}
                  transition={{
                    duration: 1.5,
                    delay: 1.5,
                  }}
                  exit={{ x: 1050, opacity: 0 }}
                >
                  <Skills />
                </motion.div>
              )}
              {isProjectsVisible && (
                <motion.div
                  key="projects"
                  initial={{ x: 1050, opacity: 0 }}
                  animate={{ x: 0, opacity: 1 }}
                  transition={{ duration: 1.5, delay: 1.5 }}
                  exit={{ x: 1050, opacity: 0 }}
                >
                  <Projects />
                </motion.div>
              )}
            </AnimatePresence>
    

    and as @ivanatias said, for keeping track of the element's presence, a key must be added to each animated element