Skip to main content

#Learning

Animations on the Web

Personal notes and experiments with Motion

animations on the web
🌱 Seedling
April 1, 2026
4 min read

WARNING

This post is a living document — I'm actively taking this course and updating my notes while experimenting as I go. Expect rough edges, incomplete sections, and the occasional half-baked thought. This if this less as a finished article and more as an open notebook you're welcome to peek into.

Table of Contents

Introduction

Notes from Emil's course Animations on the Web—specifically around Motion (formerly known as Framer-Motion).

The Basics

To animate with Motion we need to use the <motion.div> element. It’s a wrapper around the native HTML elements that allows us to animate them using Motion’s API.

JSX
<motion.div className="element" />
  • initial defines starting animation state, animate defines end state.

When working with animations you have a start and an end state. In Motion, you can define these states using the initial and animate props. The initial prop defines the starting state of the animation, while the animate prop defines the end state.

JSX
<motion.div
	initial={{ opacity: 0, scale: 0 }}
	animate={{ opacity: 1, scale: 1 }}
	className="element"
/>

INFO

When we inspect a Framer Motion animation we can see that the value we provided isn’t immediately applied. It’s interpolated, which means that the value is gradually changed. That’s because the animation is done in Javascript and not CSS.

This simple example will make our element invisible when it renders the first time (at mount time), and then immediately transition to it's ending state, presenting itself with the opacity and scale.

Transition Prop

By default, Motion will create an appropriate animation for a snappy transition based on the types of value being animated. For instance, physical properties like x or scale will be animated via a spring simulation. Whereas values like opacity or color will be animated with a tween (easing-based).

We can define our own transition using the transition prop. It takes in an object with properties like duration, type, delay, and more.

JSX
// Spring animation
<motion.div
  animate={{
    x: 100,
    transition: { type: "spring", duration: 0.5, bounce: 0.2 },
  }}
/>

// Easing animation
<motion.div
  animate={{
    x: 100,
    transition: { duration: 0.3, ease: "easeOut" },
  }}
/>

Exit animations

Exit animations in React are hard. AnimatePresence in Framer Motion allows components to animate out when they’re removed from the React tree.

It has good DX as well. All you need to do is wrap an element you want to animate out with AnimatePresence, and add the exit prop, which works the same way as initial and animate, except it defines the end state of our component when it’s removed.

JSX
import { motion, AnimatePresence } from 'motion/react';

export const MyComponent = ({ isVisible }) => (
	<AnimatePresence>
		{isVisible ? (
			<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} />
		) : null}
	</AnimatePresence>
);

It also has different modes. The wait mode can be used to animate between two elements. When you click the button, the copy icon animates out, and only after that, the checkmark animates in.

JSX
const variants = {
  hidden: { opacity: 0, scale: 0.5 },
  visible: { opacity: 1, scale: 1 },
};

// ...

<button aria-label="Copy code snippet" onClick={copy}>
  <AnimatePresence mode="wait" initial={false}>
    {copied ? (
      <motion.span
        key="checkmark"
        variants={variants}
        initial="hidden"
        animate="visible"
        exit="hidden"
      >
        <CheckmarkIcon />
      </motion.span>
    ) : (
      <motion.span
        key="copy"
        variants={variants}
        initial="hidden"
        animate="visible"
        exit="hidden"
      >
        <CopyIcon />
      </motion.span>
    )}
  </AnimatePresence>
</button>
);

With this type of animations, it’s important to include initial={false} on AnimatePresence. This tells Framer Motion not to animate on the initial render.

TIP

If an animation that involves AnimatePresence is not working as expected, make sure that you have a key prop on the element you’re animating. Otherwise, the component won’t be unmounted and the exit animation won’t be triggered.

Code

Back

Head back to writing and explore more posts.