Jimmy
Jimmy

Reputation: 3860

Avoiding framer-motion initial animations on mount

Please see this codesandbox.

I have a basic framer-motion animation where the height of a box is animated when toggled. However, I want the box to be shown by default, but when the page loads the initial animation is presented.

My question is, how do I avoid having an initial animation for a component if it should be shown on mount, but still maintain future enter and exit animations? Thanks!

Upvotes: 7

Views: 12278

Answers (5)

cody
cody

Reputation: 341

Bit old of a question, but just in case some people stumble upon this. if you are using an AnimatePresence you can use initial={false} on the AnimatePresence component. Like so,

<AnimatePresence initial={false}>
  {someCondition ? (
    <motion.h1 {...yourProps}>
      yooo
    </motion.h1>
  ) : null}
</AnimatePresence>

More info here: https://www.framer.com/motion/animate-presence/##suppressing-initial-animations

edit: doc link update (oct 20th 2024)

Upvotes: 10

Oluwaferanmi Ajiboye
Oluwaferanmi Ajiboye

Reputation: 63

The simplest method is provided in the framer motion official docs: specifiy the initial prop with the value as false


<motion.div
...
initial={false}
>

</motion.div>

https://www.framer.com/docs/animation/##enter-animations

Upvotes: 4

mavarazy
mavarazy

Reputation: 7735

For me specifying initial property in motion.div helped

<motion.div initial={{ opacity: 0, height: 0}}>
...
</motion.div>

Upvotes: 0

MapMyMind
MapMyMind

Reputation: 153

You can check if it is the components first render by:

const firstRender = useRef(true);

useEffect(() => {
 if (firstRender.current) {
      firstRender.current = false;
      return;
    }
});

If it is the first render of the component, don't pass the variants to the motion.div. In your example this would look like this:

const variants = {
  initial: {
    opacity: 0,
    height: 0
  },
  enter: {
    opacity: 1,
    height: "200px",
    transition: { duration: 0.5 }
  },
  exit: {
    opacity: 0,
    height: 0,
    transition: { duration: 0.5 }
  }
};

export const AnimatedFallback = ({ isVisible }) => {
  const firstRender = useRef(true);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
      return;
    }
  });
  
  return (
    <AnimatePresence>
      {isVisible && (
        <motion.div
          animate="enter"
          className="fallback"
          exit="exit"
          initial="initial"
          variants={firstRender.current ? {} : variants}
        >
          Suspense Fallback Component
        </motion.div>
      )}
    </AnimatePresence>
  );
};

Upvotes: 1

İlker
İlker

Reputation: 2090

I came up with this kind of solution;

1- I took variants to inside of the component

2 - I created two states for opacity and height

3 - States are initially same as where you animate to. So basically nothing happens when you first render.

4 - With useEffect, you can swap the values with the actual initial values, so after first render, the animation works.


export const AnimatedFallback = ({ isVisible }) => {
  const [opacity, setOpacity] = useState(1);
  const [height, setHeight] = useState("200px");

  const variants = {
    initial: {
      opacity: opacity,
      height: height
    },
    enter: {
      opacity: 1,
      height: "200px",
      transition: { duration: 0.5 }
    },
    exit: {
      opacity: 0,
      height: 0,
      transition: { duration: 0.5 }
    }
  };

  useEffect(()=> {
    setHeight(0)
    setOpacity(0)
  }, [])

  return (
    <AnimatePresence>
      {isVisible && (
        <motion.div
          animate="enter"
          className="fallback"
          exit="exit"
          initial="initial"
          variants={variants}
        >
          Suspense Fallback Component
        </motion.div>
      )}
    </AnimatePresence>
  );
};

Upvotes: 1

Related Questions