Well, we all have been wanted to make a customized Modal with ease but we get stuck in few places as we build it. So let's dive into creating a modal with some cool transition animation.
What are we going to use?
We will be building it from scratch so we need the following
- NEXTjs
- Framer motion
- Typescript
Initializing the NEXTjs Project
To create a NEXT project type the following lines
npx create-next-app@latest --typescript
# or
yarn create next-app --typescript
Name it as whatever you like but I am gonna name it as simple-modal for uniformity.
Then change directory by cd simple-modal
.
Then run
yarn add framer-motion
yarn dev
This will add framer motion to the project, next we can start working on it.
Setting up the workspace
Clear out all the pre-written template from the index.tsx
.
Create a Components folder and create a file called modal.tsx
.
Design your Modal component as you like. For this tutorial I have designed it in the following way.
import { FC } from "react";
const Modal: FC = () => {
return (
<div className="modal-backdrop">
<div className="modal-container">
<h1>Modal Header</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Porro
reprehenderit dolores iure facilis libero repellendus pariatur, totam
voluptate magnam dolorem assumenda soluta. Repellendus praesentium,
ducimus corporis ab odio dignissimos quam?
</p>
<div className="close">
<div></div>
<div></div>
</div>
</div>
</div>
);
};
export default Modal;
And the styling goes like this in the global.css
file
.modal-backdrop {
background: rgb(43, 43, 43);
width: 100%;
height: 100%;
display: grid;
place-items: center;
position: fixed;
}
.modal-container {
width: 25rem;
height: 32rem;
background-color: whitesmoke;
border-radius: 20px;
box-shadow: 5px 5px 10px #1e1e1e;
display: flex;
flex-direction: column;
padding: 3rem;
align-items: center;
position: relative;
}
.modal-container h1 {
font-size: 2.5rem;
color: rgb(0, 138, 92);
}
.modal-container p {
font-size: 1.25rem;
color: rgb(95, 121, 1);
line-height: 2rem;
}
.close {
position: absolute;
top: 20px;
right: 20px;
width: 2rem;
height: 2rem;
cursor: pointer;
}
.close div {
position: absolute;
height: 1.5rem;
width: 3px;
background: rgb(255, 145, 1);
border-radius: 10px;
right: 50%;
left: 50%;
}
.close div:first-child {
transform: rotate(45deg);
}
.close div:last-child {
transform: rotate(-45deg);
}
Then Import it in the index.tsx
file to see your changes.
The modal should look as below
Setting up Toggling
The simplest way to toggle a Modal to open and close state is using useState
.
So the we add useState to index.tsx
for toggling and also a button as follows:
const [isOpen, setIsOpen] = useState(false);
return (
<div className="background">
<Head>
<title>Simple Modal</title>
<meta name="description" content="Simple Modal" />
<link rel="icon" href="/favicon.ico" />
</Head>
<button className="button" onClick={() => setIsOpen(true)}>
Open Modal
</button>
<Modal isOpen={isOpen} setIsOpen={setIsOpen} />
</div>
);
Also add some button styles.
.button {
padding: 10px 15px;
background: rgb(255, 136, 0);
border-radius: 100px;
color: ghostwhite;
font-weight: 600;
letter-spacing: 3px;
font-size: 1rem;
border: none;
}
Make some changes in Modal.tsx
for render the modal conditionally.
interface Props {
isOpen: boolean;
setIsOpen: (state: boolean) => void;
}
const Modal: FC<Props> = ({ isOpen, setIsOpen }) => {
return (
<>
{isOpen && (
<div className="modal-backdrop">
<div className="modal-container">
<h1>Modal Header</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Porro
reprehenderit dolores iure facilis libero repellendus pariatur,
totam voluptate magnam dolorem assumenda soluta. Repellendus
praesentium, ducimus corporis ab odio dignissimos quam?
</p>
<div className="close" onClick={() => setIsOpen(false)}>
<div></div>
<div></div>
</div>
</div>
</div>
)}
</>
);
};
Well our Modal is ready? Not Exactly as you can see the transition is very abrupt.
Initiating magic to our Modal
Well now we import motion framer-motion
to our Modal component.
We use the motion
and AnimatePresence
from framer-motion
to create magic as follow:
- Use a prefix for our
div
component and change it tomotion.div
.<motion.div className="modal-backdrop"> <motion.div className="modal-container"> <h1>Modal Header</h1> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Porro reprehenderit dolores iure facilis libero repellendus pariatur, totam voluptate magnam dolorem assumenda soluta. Repellendus praesentium, ducimus corporis ab odio dignissimos quam? </p> <motion.div className="close" onClick={() => setIsOpen(false)}> <div></div> <div></div> </motion.div> </motion.div> </motion.div>
- Wrap the Conditional rendering of our Modal component in
AnimatePresence
.<AnimatePresence> {isOpen && ( // Modal code )} </AnimatePresence>
- Add the animations and transitions. Create the variants that will govern the animations
const backdropVariant = {
hidden: {
opacity: 0,
},
visible: {
opacity: 1,
transition: {
duration: 1,
delayChildren: 0.2, // To delay the child animation
},
},
};
const modalVariant = {
hidden: {
y: "-100vh",
},
visible: {
y: 0,
transition: {
type: "spring", // Transition type animation used is spring
stiffness: 70, // Stiffness of spring
},
},
};
There are 2 variants
- Backdrop variant is for the background overlay for the modal
- The animation for the modal itself
Also the 2 states in the variants are
hidden
which is initial state of the transitionvisible
-> the final state of the transiton
Finally pass the variants into the motion.div
components like below:
<motion.div
className="modal-backdrop"
variants={backdropVariant}
initial="hidden"
animate="visible"
exit="hidden"
>
<motion.div className="modal-container" variants={modalVariant}>
// Rest of the code as usual
Optional Close button Animation
In the Close button component i.e Cross pass add the whileHover
property as follows:
<motion.div
whileHover={{ rotate: 45 }}
className="close"
onClick={() => setIsOpen(false)}
>
<div></div>
<div></div>
</motion.div>
It will rotate the cross on Hover
.
Finally
The Modal is ready with some smooth transition. Enjoy the modal and use it where ever you can.
Github Repository - https://github.com/lawlesx/simple-modal/
Happy Coding