#Learning
Animations on the Web
Personal notes and experiments with Motion
🌱 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.
<motion.div className="element" />initialdefines starting animation state,animatedefines 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.
<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.
// 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.
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.
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
AnimatePresenceis not working as expected, make sure that you have akeyprop on the element you’re animating. Otherwise, the component won’t be unmounted and the exit animation won’t be triggered.
Back
Head back to writing and explore more posts.

