As web developer, learning custom modal with animation is good thing. Here I will explain how i created react modal with tailwindcss and nextjs. I usually used react-bootstrap for UI development, there react-modal is awesome. Here I don't want to install another UI building like React-bootstrap, I have already using tailwindcss.
How to create modal using tailwind in nextjs or react
Before anything, what I am using for here, NextJs with TypeScript and Tailwindcss no other dependency. If want to check in github click here
Setup or installation
for nextjs we can create npx create-next-app name or any other cli tool like CRA or npm vite
npx create-next-app react-modal --ts
for tailwind check here
Code
I am using all single file to write all style and component instead of creating different file and directory. First clear all the TSX or JSX in App file. I have Head and title in main file, it doesn't matter for modal.
For modal show or not show I am using the useState from react and default is false, using another useState for list of skill or any strings of array for displaying when i add another skill.
const [showModal, setShowModal] = useState(false);
const [skills, setSkills] = useState<string[(["JavaScript",
"TypeScript","NodeJs","Python","HTML","CSS",]);
For above state to change, I created the two helper function for changing showModal state and adding new skill in skills state.
const modalToggle = () => {
setShowModal(!showModal);
};
const addSkill = (s: string) => {
setSkills([...skills, s]);
};
Home page
I have created the button with different tailwind style for onClick event trigger modalToggle to change.
Used the ternary or conditional operator to show the ReactModal component if true show or null, props drill the two helper function to ReactModal component to access. And li for showing all skill of skills state.
<div>
<Head>
<title>next modal tailwind</title>
<meta name="description" content="React modal with tailwindcss" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className="flex justify-center p-2">
<button
onClick={modalToggle}
className="mt-2 border px-5 py-1.5 bg-blue-400 rounded-lg font-semibold text-white hover:bg-blue-700"
>
Add List
</button>
</div>
{showModal ? (
<ReactModal toggle={modalToggle} addSkill={addSkill} />
) : null}
<h3 className="text-2xl font-semibold underline text-gray-700">
My skills :{" "}
</h3>
<div className="py-1 5" />
<ul className="list-disc ml-10">
{skills.map((skill, idx) => (
<li key={idx}>{skill}</li>
))}
</ul>
</div>
modal feature ?
The modal working is similar to popup. When clicking the button it show the modal. In modal it can form or Oauth, by submitting it close the modal or click close button it can be X or close. Another feature by clicking another then modal it will close the modal.
How modal work ?
Most of modal or popup work on the z-index feature of css that allow us to 3-dimension of web page. For z-index to work it have to position property of css like absolute or relative. If z-index on below html can't access like over don't work.
How to build modal with React or tailwindcss
For me, I used the ReactModal component with some position absolute and top-0,button-0,left-0,right-0 in tailwind it is inset-0.
First create a component, I used React.FC type to functional component.It show the error if proper tsx don't return. In typescript we have to pass prop generic to access the property in props that passed in ReactModal component above. destructure the props toggle and addSkill feature.
const ReactModal: FC<{ toggle: () => void; addSkill: (s: string) => void }> = ({
toggle,
addSkill,
}) => {
*State for modal section *
Here used three hook for state management. two useRef for background of modal and input field for skill add. another one for closing animation. If toogle false it will immediately close the modal for smooth transition.
const modalRef = useRef(null);
const inputRef = useRef<HTMLInputElement>(null);
const [close, setClose] = useState(false); // closing animation
Animation in modal
Tailwind animation are limited not great for all situation. To smooth animation i used the css instead of make custom tailwind.
NOTE
- for starting animation used delay 0s in animation otherwise it will render immediately and show animation.
- for closing animation, animation time more then delay closing
For me closing animation is tricky, therefore i used another state for showing closing animation, I used promise to delay 1s in that time i used delay animation to show. Before the toggle, change the close state to true to render closing animation.
.modal-body-animation {
animation: updown 800ms ease-in-out 0s 1;
}
.modal-body-animation-close {
animation: downup 1.1s ease-in-out 0s 1;
}
@keyframes updown {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0%);
}
}
@keyframes downup {
0% {
transform: translateY(0%);
}
100% {
transform: translateY(-200%);
}
}
Closing when click in background and form submit
As below code display, when mouseEvent trigger it check whether it on background, we can access via useRef hook to find out event. similar to submit event and trigger close.
const closeHelper = async () => {
setClose(true);
await new Promise((r) => setTimeout(r, 1000));
toggle();
};
const closeModalBackground = (e: MouseEvent) => {
if (modalRef.current === e.target) {
closeHelper();
}
};
const Submit = (e: FormEvent) => {
e.preventDefault();
const skill = inputRef.current?.value;
if (skill !== undefined && skill?.trim() !== "") {
addSkill(skill!);
}
closeHelper();
};
return (
<div
ref={modalRef}
onClick={(e) => closeModalBackground(e)}
className="absolute inset-0 z-10 flex flex-col items-center"
>
<div className="h-1/5" />
<div
className={`bg-indigo-700 sm:w-1/2 h-40 p-3 rounded-lg relative modal-body-animation ${
close ? "modal-body-animation-close" : ""
}`}
>
<button
onClick={closeHelper}
className="absolute top-0 right-0 text-xl font-semibold text-red-500 px-3"
>
X
</button>
<h3 className="text-xl font-semibold text-white text-center">
Add your new skill
</h3>
<div className="py-1 5" />
<form onSubmit={(e) => Submit(e)}>
<input
className="block w-1/2 mx-auto px-2 py-1.5 rounded outline-none"
type="text"
placeholder="add skill"
ref={inputRef}
/>
<button className="block mx-auto mt-2 py-1.5 px-3 rounded text-white font-semibold border hover:bg-indigo-400">
Add skill
</button>
</form>
</div>
</div>
);
I hope you enjoy the blog
Top comments (0)