Animate Elements Entering and Exiting the DOM with Framer Motion
If you’re just getting started with Framer Motion, one of the first (and most obvious) things that will come to mind to animate is the entrance and exit of a component as it enters and leaves the DOM.
And for good reason! In actual/real/physical life, things don’t just appear. They “come into view”. Either by gradually getting closer, or appearing from behind something else, or materializing in some other transition-based fashion. Similarly, when things go out of view, they transition to that state… they don’t just instantaneously cease from existing.
Adding just a touch of this transitioning to our React components through pleasant & subtle animations goes a long away to creating a user interface that feels natural and enjoyable to use. And it’s a great place to get started as we explore the Framer Motion animation library.
Getting Started
For the sake of this article, we’re going to assume you have a React project set up, and Framer Motion already installed. You can find more info here if you need help installing.
Creating Our First Animation
Let's start with a simple example: a button that toggles the visibility of a card.
import React, { useState } from "react";
function AnimatedCard() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? "Hide" : "Show"} Card
</button>
{isVisible && (
<div className="card">
<h2>Hello world!</h2>
<p>This card will be animated ✨</p>
</div>
)}
</div>
);
}
Nothing crazy happening here. When you click the button, it updates isVisible
, which is used to control whether we actually render the card to the DOM or not. Basic React… and very not-animated.
Animating As It Enters
To animate our card as it appears on the DOM, we’ll need to import the motion
from Framer Motion:
import { motion } from "framer-motion";
And update our card to use a <motion.div>
rather than just a div
:
<motion.div className="card">
<h2>Hello world!</h2>
<p>This card will be animated ✨</p>
</motion.div>
What on earth is this <motion.div>
thing? This is how we turn on the magic!
By using a <motion.div>
, Framer Motion is wrapping a normal ol’ div with its own component, which can accept certain animation properties, and will handle all the tricky animation logic, state management, and DOM manipulation behind the scenes.
Here we’re wrapping a div
, but you can used this motion
wrapper with any valid DOM element you need (motion.li
, motion.button
, motion.span
, etc)
If you’re ever troubleshooting why an animation isn’t working, the first thing to check is that you’ve remembered to update your element to its
motion
-based equivalent. If I had a nickel for every time that was the issue behind my non-working animation… 💰💰💰
Now that we’re all set up with our motion.div
, it’s time for the fun part… actually animating!
To animate the entrance of our component, we’ll add initial
and animate
properties to our motion.div
, and pass in objects with the values we want to change.
<motion.div
initial={{ opacity: 0, scale: 0.8, y: 50 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
className="card"
>
<h2>Hello world!</h2>
<p>This card will be animated ✨</p>
</motion.div>
When our div
is placed on the DOM, Framer motion will handle transitioning it from the values in the initial
object, to the values in the animate
object, and we’ll get a nice fade/scale/slide in effect.
(Example?)
Animating As It Exits
Since we can remove this card with our button, it would be nice to also animate its exit as it leaves the DOM. This is typically a tricky thing to do with React components, but Framer Motion helps make it much simpler. We’ll need to make two changes:
- Wrap our conditional logic area in Framer Motion’s
<AnimatePresence>
component - Add an exit prop with the values we’d like transitioned when it leaves.
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0, y: 50, scale: 0.3 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 20, scale: 0.5 }}
className="card"
>
<h2>Hello world!</h2>
<p>This card will be animated ✨</p>
</motion.div>
)}
</AnimatePresence>
And here’s our entire component at this point:
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
function AnimatedCard() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? "Hide" : "Show"} Card
</button>
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0, scale: 0.8, y: 50 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.8, y: 50 }}
transition={{ duration: 0.3 }}
className="card"
>
<h2>Hello world!</h2>
<p>This card will be animated ✨</p>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
Let's break down what's happening here:
- We use the
useState
hook to manage the visibility of our card. - The
AnimatePresence
component from Framer Motion allows us to animate elements as they're removed from the DOM. - We use
motion.div
instead of a regulardiv
to enable animations. - The
initial
prop defines the starting state of our animation. - The
animate
prop defines the final state of our animation. - The
exit
prop defines how the element should animate when it's being removed from the DOM. - The
transition
prop allows us to customize the duration and easing of our animation.
Here’s a CodeSandbox of our entire component to play around with:
Conclusion
Congrats, you’ve learned some basic animation with Framer Motion! With a little bit of practice these patterns will become second nature, and you’ll be amazed at the animating potential at your fingertips.
Just remember to keep your animation touch light, and don’t overdo it.
Happy animating!