Animated Modal with Framer Motion
Satel
Posted on May 18, 2022
Framer Motion is a React library for adding declarative animations to your React applications. It provides a variety of components that wrap plain HTML elements to extend them with animation superpowers.
I would like to build a modal with Framer Motion with a variety of different animation styles.
Setup
Install Framer motion package in your React application
npm i framer-motion
Animated Modal
Trigger Button
Create a button that can when clicked will open a modal. Define a stateful value modalOpen
to keep track of the open/close state. In this example, we use the motion.div
component also animate the button itself.
function App() {
const [modalOpen, setModalOpen] = useState(false);
const close = () => setModalOpen(false);
const open = () => setModalOpen(true);
return (
<div>
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="save-button"
onClick={() => (modalOpen ? close() : open())}
>
Launch modal
</motion.button>
</div>
)
}
Backdrop
Create a component to serve as the backdrop for the modal. This component will be a motion.div
component that fades in and out. It takes the onClick
prop to close the modal when the backdrop is clicked.
import { motion } from "framer-motion";
const Backdrop = ({ children, onClick }) => {
return (
<motion.div
onClick={onClick}
className="backdrop"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
{children}
</motion.div>
);
};
export default Backdrop;
Modal Window
The modal component uses the Backdrop, then has its own motion.div
component that animates in and out. The animation states are defined in the dropIn
object. Framer will transition from one state to the other when it is mounted in the DOM.
Also notice how stopPropagation
is called when the modal is clicked - this prevents it from closing when the modal is clicked.
import { motion } from "framer-motion";
import Backdrop from "../Backdrop";
const dropIn = {
hidden: {
y: "-100vh",
opacity: 0,
},
visible: {
y: "0",
opacity: 1,
transition: {
duration: 0.1,
type: "spring",
damping: 25,
stiffness: 500,
},
},
exit: {
y: "100vh",
opacity: 0,
},
};
const Modal = ({ handleClose, text }) => {
return (
<Backdrop onClick={handleClose}>
<motion.div
onClick={(e) => e.stopPropagation()}
className="modal orange-gradient"
variants={dropIn}
initial="hidden"
animate="visible"
exit="exit"
>
<p>{text}</p>
<button onClick={handleClose}>Close</button>
</motion.div>
</Backdrop>
);
};
export default Modal;
Animate Presence
Framer Motion has a built in AnimatePresence component that can handle animations for components that get added/removed from the DOM - we need it to animate the removal of a modal component.
<AnimatePresence
// Disable any initial animations on children that
// are present when the component is first rendered
initial={false}
// Only render one component at a time.
// The exiting component will finish its exit
// animation before entering component is rendered
exitBeforeEnter={true}
// Fires when all exiting nodes have completed animating out
onExitComplete={() => null}
>
{modalOpen && <Modal modalOpen={modalOpen} handleClose={close} />}
</AnimatePresence>
Extra Animations
Flip
const flip = {
hidden: {
transform: "scale(0) rotateX(-360deg)",
opacity: 0,
transition: {
delay: 0.3,
},
},
visible: {
transform: " scale(1) rotateX(0deg)",
opacity: 1,
transition: {
duration: 0.5,
},
},
exit: {
transform: "scale(0) rotateX(360deg)",
opacity: 0,
transition: {
duration: 0.5,
},
},
};
Newspaper
const newspaper = {
hidden: {
transform: "scale(0) rotate(720deg)",
opacity: 0,
transition: {
delay: 0.3,
},
},
visible: {
transform: " scale(1) rotate(0deg)",
opacity: 1,
transition: {
duration: 0.5,
},
},
exit: {
transform: "scale(0) rotate(-720deg)",
opacity: 0,
transition: {
duration: 0.3,
},
},
};
Bad Suspension
const badSuspension = {
hidden: {
y: "-100vh",
opacity: 0,
transform: "scale(0) rotateX(-360deg)",
},
visible: {
y: "-25vh",
opacity: 1,
transition: {
duration: 0.2,
type: "spring",
damping: 15,
stiffness: 500,
},
},
exit: {
y: "-100vh",
opacity: 0,
},
};
Posted on May 18, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.