Back to overview

Framer Motion Examples

AnimationReact
article cover

Animate through Multiple States with Keyframes

export default function Index() {
  return (
    <motion.div
      animate={{opacity: 1, y: [-200, 0, 200, 0, -200, 0]}}
      transition={{duration: 2, times: [0, 0.2, 0.3, 0.7, 0.9, 1]}}
    >
      up and down bounce
    </motion.div>
  );
}
  • times is an array from 0 to 1, like keyframe percentage.

  • duration tells the whole animation time.

  • at time 0, div is at -200

  • at 20%, div is at 0

  • at 30%, div is at 200

Initial Prop to Define the Beginning State of Animations

export default function Index() {
  return (
    <motion.div
      initial={{opacity: 0, y: -200}}
      animate={{opacity: 1, y: 0}}
      transition={{duration: 0.4, ease: 'easeOut', type: 'tween'}}
    >
      fade in from top
    </motion.div>
  );
}

initial is the initial state of the element, and animate is the final end state.

Use this for hero headings, description, etc

Variants, making states semantic and reusable

Let's update the above example with variants:

const fadeInVariant = {
  hidden: {opacity: 0, y: -200},
  visible: {opacity: 1, y: 0, transition: {duration: 0.5, ease: 'easeOut', type: 'tween'}},
};

export default function Index() {
  return (
    <motion.div initial="hidden" animate="visible" variants={fadeInVariant}>
      fade in from top
    </motion.div>
  );
}

Animate Elements Removed by AnimatePresence and Exit

Let's say we have a todo list whose item will fade in when a new one is added and fade out when removed. We need to wrap AnimatePresence with the list and add exit prop indicating the exit state.

const fadeInVariant = {
  hidden: {opacity: 0, y: -200},
  visible: {opacity: 1, y: 0, transition: {duration: 0.5, ease: 'easeOut', type: 'tween'}},
};

export default function Index() {
  const items = ['item 1', 'item 2'];
  return (
    <ul>
      <AnimatePresence>
        {items.map((i) => (
          <motion.li
            key={i}
            initial="hidden"
            animate="visible"
            exit="hidden"
            layoutId={i}
            variants={fadeInVariant}
          ></motion.li>
        ))}
      </AnimatePresence>
    </ul>
  );
}

Animate Elements When Their Layout Changes with Framer Motion layoutId. Use the layoutId prop to automatically animate layout changes across, and between, multiple components.

I think layoutId value could be the key value. One element exiting will need it. If only 1 element exists for the animation, I don't think it is needed.

Stagger

The custom prop let's you create dynamic variant values to animate each component differently.

const fadeInVariant = {
  hidden: {opacity: 0, y: -200},
+  visible: (custom) => ({opacity: 1, y: 0, transition: {duration: 0.5, ease: 'easeOut', type: 'tween', delay: custom}}),
};

export default function Index() {
  const items = ['item 1', 'item 2'];
  return (
    <ul>
      <AnimatePresence>
        {items.map((i, index) => (
          <motion.li
            key={i}
            initial="hidden"
            animate="visible"
            exit="hidden"
            layoutId={i}
+            custom={(index + 1) * 0.2}
            variants={fadeInVariant}
          ></motion.li>
        ))}
      </AnimatePresence>
    </ul>
  );
}

Create Micro Interactions with Gesture Props

Framer Motion provides helper props like whileHover & whileTap, that will temporarily animate a component to a visual state when that gesture is happening.

const fadeInVariant = {
  hidden: {opacity: 0, y: -200},
  visible: (custom) => ({opacity: 1, y: 0, transition: {duration: 0.5, ease: 'easeOut', type: 'tween', delay: custom}}),
};

export default function Index() {
  const items = ['item 1', 'item 2'];
  return (
    <ul>
      <AnimatePresence>
        {items.map((i, index) => (
          <motion.li
            key={i}
            initial="hidden"
            animate="visible"
            exit="hidden"
            layoutId={i}
            custom={(index + 1) * 0.2}
            variants={fadeInVariant}
+            whileHover={{scale: 1.05}}
+            whileTap={{scale: 1.1}}
          ></motion.li>
        ))}
      </AnimatePresence>
    </ul>
  );
}

SVG path animation

import {motion} from 'framer-motion';

const svgVariants = {
  start: {opacity: 0, pathLength: 0},
  end: {opacity: 1, pathLength: 1, transition: {duration: 2, ease: 'easeInOut'}},
};

export default function MenuButton({onClick}: {onClick: () => void}) {
  return (
    <button onClick={onClick}>
      <motion.svg initial={false} className="group h-10 w-10" viewBox="0 0 56 56" fill="none">
        <motion.circle
          initial="start"
          animate="end"
          variants={svgVariants}
          cx="28"
          cy="28"
          r="27"
          className="stroke-gray-300 group-hover:stroke-gray-900 dark:stroke-gray-600 dark:group-hover:stroke-gray-100"
          strokeWidth="2"
        />
        <motion.path
          initial="start"
          animate="end"
          variants={svgVariants}
          d="M17.8182 35.6364H38.1818M17.8182 20.3636H38.1818H17.8182ZM17.8182 28H38.1818H17.8182Z"
          stroke="currentColor"
          strokeWidth="2"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
      </motion.svg>
    </button>
  );
}

Path morphing

Loading Code Editor

Scroll-based animations

Create declarative, reactive chains of MotionValues that can update as a result of animations and/or gestures.

const {scrollYProgress} = useViewportScroll();

return <motion.path style={{pathLength: scrollYProgress}} />;

Server-side rendering

The animated state of a component will be rendered server-side to prevent flashes of re-styled content after your JavaScript loads.

<motion.div initial={false} animate={{x: 100}} />

MotionValues

Create declarative, reactive chains of MotionValues that can update as a result of animations and/or gestures.

MotionValues track the state and velocity of animating values.

const x = useMotionValue(0);

const xinput = [-200, 0, 200];
const opacityOutput = [0, 1, 0];
const colorOutput = ['#f00', '#fff', '#0f0'];

const opacity = useTransform(x, xInput, opacityOutput);
const color = useTransform(x, xInput, colorOutput);

return <motion.div drag="x" style={{x, opacity, color}} />;

initial x is 0, the opacity will be 1. When dragging over to -200 or 200, opacity will be 0.

Loading Code Editor

Scroll-triggered animations

Motion also provides a whileInView prop that defines a visual state to animate to while a component is in the viewport.

<motion.div initial={{opacity: 0}} whileInView={{opacity: 1}} viewport={{once: true}} />

Scroll-linked animations

The useViewportScroll hook provides four read-only MotionValues, two that return the viewport's x/y scroll position in pixels, and two that return it as progress value between 0 and 1.

You can use these MotionValues to declaratively drive features like progress indicators or parallax effects.

import {motion, useViewportScroll} from 'framer-motion';

export const CircleIndicator = () => {
  const {scrollYProgress} = useViewportScroll();

  // a svg progress indicator
  return <motion.path d="M 0, 20 a 20, 20 0 1,0 40,0 a 20, 20 0 1,0 -40,0" style={{pathLength: scrollYProgress}} />;
};

Layout animation

To automatically animate the layout of a motion component when its size or position changes, give it a layout prop.

<motion.div layout />
Loading Code Editor

Shared layout animations

When a new motion component is added, it can be automatically animated from another one by giving them both the same layoutId prop.

isSelected ? <motion.div layoutId="underline" /> : null;
Loading Code Editor

Reference