How to create exit animations with Framer Motion
In this article, you'll learn how to implement exit animations using the exit prop and the AnimatePresence component.
Framer Motion is a powerful library built on top of React that simplifies the creation of animations within your user interface. While core React functionality allows for animations and transitions using CSS, Framer Motion offers a more feature-rich toolset designed to implement dynamic and performant animations.
In this article, you'll learn how to implement exit animations using the exit
prop and the AnimatePresence
component. We'll progressively build upon this foundation, demonstrating how to create more advanced exit animations with custom transitions and delayed effects. At the end of this, you'll build a simple pop-up modal using all you learn. I hope you have a lovely read.
Get Started
Prerequisites:
Node.js and npm (or yarn) installed on your system.
An existing React project.
Basic knowledge of Framer Motion
- Install Framer Motion:
In your project directory, install Framer Motion using either npm or yarn:
Using npm:
npm install framer-motion
Using yarn:
yarn add framer-motion
- Create a basic component:
Let's create a simple component to play around with. Inside the src
folder, create a new file named my-component.tsx
. Here's a basic structure for our component:
import React from 'react';
const MyComponent = () => {
return (
<div className="w-20 h-20 bg-green-600 rounded-full cursor-pointer" />
);
};
export default MyComponent;
- Import Framer Motion:
To make your component move with Framer Motion, you need to import the motion
component. Update your my-component.tsx
file to include this line at the top:
import { motion } from 'framer-motion';
The motion
import from framer-motion
lets you convert an ordinary HTML tag like div
to a motion.div
tag that allows you to animate your React elements.
There you have it! You now have a basic React project set up with Framer Motion and are ready to create awesome exit animations!
Can CSS Handle Exit Animations?
Sure, CSS transitions seem like a natural fit for exit animations in React at first glance. You can define styles for an element and smoothly transition them to disappear, creating a fade-out or slide-out effect.
However, here's the catch: When you try to hide a React component during an exit animation using CSS, you run into a roadblock. React, by design, removes components from the Document Object Model (DOM) entirely when they are hidden. This removal from the DOM makes them unavailable for animation by CSS. CSS transitions rely on manipulating elements within the DOM, so a removed element can't be smoothly animated.
This presents a challenge for basic exit animations with CSS in React. While CSS might seem viable, it can't handle the core functionality of animating a disappearing component due to React's behavior. This is where Framer Motion steps in, offering a solution specifically designed for handling animations alongside React's component lifecycle.
Using AnimatePresence
Remember, React entirely removes hidden components from the Document Object Model (DOM). This disrupts the animation process if you directly apply exit animations. AnimatePresence
solves this challenge by acting as a wrapper around your components.
How to use AnimatePresence
:
Define the Exit Animation:
- We start by defining the exit animation itself using the
exit
prop within Framer Motion variants. This prop specifies the animation that plays when the component exits the scene.
- We start by defining the exit animation itself using the
Here's an example:
const exitAnimation = {
opacity: 0, // Fade out to zero opacity
};
Wrap with
AnimatePresence
:Next, we wrap our components with
AnimatePresence
. This ensures that even when a component is hidden, it remains in the DOM momentarily, allowing Framer Motion to complete the exit animation before it's truly removed.
Here's how it looks in practice:
import { AnimatePresence } from 'framer-motion';
export function App() {
const [visible, setVisible] = useState(true);
const exitAnimation = {
opacity: 0, // Fade out to zero opacity
};
return (
<AnimatePresence>
{visible && (
<motion.div
exit={exitAnimation}
className="w-20 h-20 bg-green-600 rounded-full cursor-pointer"
onClick={() => setVisible(false)}
/>
)}
</AnimatePresence>
);
}
With AnimatePresence
:
Without AnimatePresence
:
In this example:
We wrap the
motion.div
withAnimatePresence
.The
motion.div
has the definedexitAnimation
applied using theexit
prop, which fades out the element.When the component is clicked (toggling
visible
tofalse
), AnimatePresence ensures the exit animation plays before removing the element from the DOM.
This is just a basic example, of course. Framer Motion allows you to animate various properties like opacity, scale, translate (position), and more, giving you the freedom to create a wide variety of exit animations.
Advanced Exit Animations
We've explored the basics of defining exit animations with Framer Motion. Now, let's learn some advanced techniques to create better exit animations:
Fine-tune with Custom Transitions:
The transition
property within the exitAnimation
object offers even more control over your animation. You can define custom easing functions to control the speed and flow of the animation:
const exitAnimation = {
opacity: 0,
transition: { duration: 0.5, ease: "easeInOut" }, // Use "easeInOut" for smooth start and end
};
Here, we've added the ease: "easeInOut"
property to the transition
. This tells the animation to ease in and out smoothly, creating a more natural feel.
Combining Multiple Animation Properties:
Don't limit yourself to a single animation property! Framer Motion allows you to combine multiple properties within your exitAnimation
object:
const exitAnimation = {
opacity: 0,
x: "-100vw", // Slide off-screen to the left
rotate: 90, // Rotate 90 degrees
transition: { duration: 0.7 },
};
In this example, we're fading out the component (opacity: 0
), sliding it off-screen to the left (x: "-100vw"
), and adding a 90-degree clockwise rotation (rotate: 90
) for a more dramatic exit.
Delayed Exits for Multiple Elements:
An exit animation can become even more engaging when applied to multiple elements. Framer Motion offers two techniques to create a cascading or wave-like effect: individual delays and stagger.
export function MyComponent() {
const [visible, setVisible] = useState(true);
const exitAnimationParent = {
opacity: 0,
x: "-25vw",
rotate: 90,
transition: {
duration: 0.5,
ease: "easeInOut",
delay: 0.5,
},
};
const exitAnimationChild = {
opacity: 0,
x: "25vw",
rotate: 90,
};
return (
<AnimatePresence>
{visible && (
<motion.div
exit={exitAnimationParent}
className="w-20 h-20 bg-green-600 cursor-pointer rounded-2xl flex justify-evenly items-center"
onClick={() => setVisible(false)}
>
<motion.div
exit={exitAnimationChild}
transition={{ duration: 0.5, ease: "easeInOut", delay: 0.2 }}
className="w-7 h-7 bg-white rounded cursor-pointer"
/>
<motion.div
exit={exitAnimationChild}
transition={{ duration: 0.5, ease: "easeInOut" }}
className="w-7 h-7 bg-white rounded cursor-pointer"
/>
</motion.div>
)}
</AnimatePresence>
);
}
The provided component demonstrates delayed exits for two child elements within a parent container. Here's a breakdown of what's going on:
Parent Container Animation:
The parent container (
motion.div
) uses theexitAnimationParent
variant. The animation properties and configuration remain the same:Fades the container out with
opacity: 0
.Slides the container off-screen by 25 viewports to the left with
x: "-25vw"
.Rotate the container 90 degrees clockwise with
rotate: 90
.The animation has a duration of 0.5 seconds, uses an "easeInOut" easing function, and has a delay of 0.5 seconds before starting.
Child Element Animations:
Each child element (
motion.div
) still uses theexitAnimationChild
variant defining opacity and x-axis translation.However, the key difference is the individual
transition
prop for each child.The first child element now has a slight delay of 0.2 seconds within its transition.
The second child element retains the original transition properties (0.5 seconds duration and "easeInOut" easing).
These are just a few ways to create advanced exit animations with Framer Motion. While individual delays create a cascading effect, Framer Motion offers a more scalable approach for complex scenarios: staggerChildren
and delayChildren
. We won't cover those in this tutorial.
Building a Modal Component with Exit Animation
Modal components are a common UI element for displaying additional content or functionality within an overlay. With Framer Motion, we can add a touch of polish with a smooth exit animation when the modal is dismissed.
A modal component typically consists of two parts:
Backdrop: A dimmed background element that creates a visual distinction between the modal and the rest of the application content.
Modal Content: The main content area displayed within the modal, often containing information, forms, or interactive elements.
Creating the Modal Component:
Here's a basic structure for our modal component with Framer Motion:
import { motion } from "framer-motion";
interface ModalProps {
setShowModal: (showModal: boolean) => void;
}
const Modal = ({ setShowModal }: ModalProps) => {
return (
<motion.div
className="overflow-y-auto overflow-x-hidden fixed top-0 bottom-0 mx-auto my-auto right-0 left-0 z-50 justify-center items-center w-full bg-black/50 md:inset-0 flex"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.div
className="h-[270px] w-full relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl sm:my-8 sm:w-full sm:max-w-lg flex flex-col items-center justify-center px-4 pb-4 pt-5 sm:p-6 sm:pb-4 gap-5"
initial={{ scale: 0.5 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
>
<div className="bg-green-100 rounded-full p-3">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
aria-hidden="true"
className="text-green-700 w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M4.5 12.75l6 6 9-13.5"
></path>
</svg>
</div>
<h4 className="font-semibold text-lg">Payment successful</h4>
<p className="text-center w-[300px] -mt-2 leading-snug text-gray-600">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Sit esse
dolorem officia.
</p>
<button
type="button"
className="inline-flex w-full justify-center rounded-md bg-[#4f46e5] px-3 py-2 text-white shadow-sm hover:bg-[#3f38b5] transition-colors text-md font-bold "
onClick={() => setShowModal(false)}
>
Close modal
</button>
</motion.div>
</motion.div>
);
};
export default Modal;
Explanation:
Initial State: The modal content starts with
opacity: 0
(invisible) and ascale: 0.5
(scaled down to 50%).Animate State: When the modal opens, the content animates to
opacity: 1
(fully visible) andscale: 1
(normal size).Exit State: When the modal closes, the content animates back to
opacity: 0
(invisible) andscale: 0
(scaled down), creating a shrinking and fading-out effect.
Adding the Modal to Your Application:
Now you can integrate this Modal
component into your application. Here's an example:
import { useState } from "react";
import { AnimatePresence } from "framer-motion";
import Modal from "./modal";
export default function App() {
const [showModal, setShowModal] = useState(false);
return (
<div className="w-full h-[100vh] flex items-center justify-center relative">
<button
className="inline-flex w-full justify-center rounded-md bg-[#4f46e5] px-3 py-2 text-md font-bold text-white shadow-sm hover:bg-[#3f38b5] sm:w-auto"
onClick={() => setOpen(!open)}
>
Open modal
</button>
<AnimatePresence>
{open && <Modal setOpen={setOpen} />}
</AnimatePresence>
</div>
);
}
In this example, clicking the "Open modal" button sets showModal
to true, triggering the modal to open with its scale-up animation. Clicking the "Close modal" button sets showModal
to false and initiates the modal's exit animation.
This is a basic example, of course. You can customize the variants and animation properties to create different exit effects.
Conclusion
If you made it all the way here, congrats!
Hopefully, you've learned how easy and intuitive implementing exit animations using Framer motion is. Let's recap what we covered:
We learned why we couldn't use plain CSS transitions to create exit animations in React.
We learned how
AnimatePresence
ensures the exit animation plays before the element is removed from the DOM.We explored how to implement more advanced exit animations using custom easing functions, multiple properties, and transition delays.
Finally, we built a simple modal component using all we learned in this guide.
Framer Motion is a powerful library; this guide has just scratched the surface! You can explore state changes and gestures and even build complex animation sequences. Check out the documentation here.
Remember, though, that animations should always be intentional. They can be a fantastic tool to guide users, communicate app state, and enhance the overall experience. But avoid going overboard โ animations shouldn't slow down your app or become distracting. Aim for a balance that doesn't impede on user experience.
I'd love to see what you build with your new knowledge. Have fun, and thanks for reading!
I would greatly appreciate any constructive feedback you may have. Feel free to share your thoughts! I'm happy to write if you found this helpful and are interested in more Framer Motion-related content. In the coming weeks, you can also expect more blog articles exploring exciting web technologies I come across. I hope you had a lovely read. ๐ค