Image and Delete Modal Components with React and Tailwind CSS

Modals are essential components in SaaS applications (dashboards, CMS, LMS). It is used for confirmations, alerts, or additional interactions. A well-designed modal can enhance user experience while maintaining a clean and reusable codebase.

In this guide, I’ll show you how to create a reusable modal component using React and Tailwind CSS. We’ll follow best React practices by first building a customizable wrapper component for the modal, which will help us create a basic outline (overlay + close button) for each modal component.

Whether you’re developing a personal project, working for a client, or enhancing your design system, this tutorial simplifies modal management in React applications.


Creating the Modal Wrapper Component

The ModalWrapper is the foundation for all modals in your application. It provides the structure (overlay, close button) and ensures consistent behavior.

The ModalWrapper acts as a base structure for all modals. It handles the overlay and ensures consistent styling across different modals in your app.

It accepts children as props, we can create any modal content of out it. We don't have to repeat the code for basic styling every time.

Code for ModalWrapper

modal-wrapper.tsx
1import { X } from 'lucide-react' 2import React from 'react' 3import useDisableScroll from '../../hooks/useDisableScroll' 4import useClickOutside from '../../hooks/useClickOutside' 5 6type ModalWrapperProps = { 7 children: React.ReactNode 8 onClose: () => void 9} 10 11const ModalWrapper: React.FC<ModalWrapperProps> = ({ children, onClose }) => { 12 const modalRef = useClickOutside(onClose) 13 useDisableScroll() 14 15 return ( 16 <div className="fixed inset-0 z-[9999] flex h-full w-full items-center justify-center bg-black bg-opacity-50"> 17 <div 18 ref={modalRef} 19 className="animate-zoom relative min-h-[200px] w-[40%] max-w-md rounded-2xl bg-white p-10 shadow-lg sm:w-11/12 sm:p-8" 20 > 21 <button 22 className="absolute right-4 top-4 text-gray-500 hover:text-gray-800" 23 onClick={onClose} 24 aria-label="Close" 25 > 26 <X size={24} /> 27 </button> 28 {children} 29 </div> 30 </div> 31 ) 32} 33 34export default ModalWrapper

The above modal wrapper component has three functionalities:

  1. Animates the modal on open and close.
  2. Disables scrolling.
  3. Closes the modal when clicking outside.

Whatever modal we create from the modal wrapper will include all the above functionalities.

Adding Modal Animations in Tailwind CSS

We added animations to our modal using in tailwind.config.js. We created a zoom effect that makes the modal grow when it opens.

tailwind.config.js
1/** @type {import('tailwindcss').Config} */ 2export default { 3 content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 4 theme: { 5 extend: { 6 animation: { 7 zoom: 'zoom 0.1s ease-in-out', 8 }, 9 keyframes: { 10 zoom: { 11 '0%': { 12 transform: 'scale(0)', 13 }, 14 '100%': { 15 transform: 'scale(1)', 16 }, 17 }, 18 }, 19 }, 20 }, 21 plugins: [], 22}

Disable Scroll When Modal Opens

To stop the background from scrolling when the modal is open, we created a custom hook useDisableScroll that set the overflow style of the body to hidden. Once the modal is closed, reset the overflow style to unset.

hooks/useDisableScroll.ts
1import { useEffect } from 'react' 2 3const useDisableScroll = () => { 4 useEffect(() => { 5 document.body.style.overflow = 'hidden' 6 7 return () => { 8 document.body.style.overflow = 'unset' 9 } 10 }) 11} 12 13export default useDisableScroll

Close Modal on Outside Click

To close the modal when clicking outside of it, we created a custom hook useClickOutside that check if the click happened outside the modal's container. If it did, we trigger the close function.

hooks/useClickOutside.ts
1import { useEffect, useRef } from 'react' 2 3const useClickOutside = (handler: () => void) => { 4 const ref = useRef<HTMLDivElement | null>(null) 5 6 useEffect(() => { 7 const handleClickOutside = (event: MouseEvent) => { 8 if (ref.current && !ref.current.contains(event.target as Node)) { 9 handler() 10 } 11 } 12 13 document.addEventListener('mousedown', handleClickOutside) 14 return () => { 15 document.removeEventListener('mousedown', handleClickOutside) 16 } 17 }, [handler]) 18 19 return ref 20} 21 22export default useClickOutside

Delete Modal Component with Tailwind CSS

This modal is specifically for confirmations before delete an item from an app. It uses the ModalWrapper to display its content and actions.

Tailwind delete modal component Preview

It includes:

  • A semi-transparent background overlay.
  • A close icon in the top-right corner.
  • Title to explain the modal action.
  • Action buttons for "Delete" and "Cancel."

Code for Delete Modal

delete-modal.tsx
1import React from 'react' 2import ModalWrapper from './ModalWrapper' 3 4interface DeleteModalProps { 5 handleDeleteModal: () => void 6} 7 8const DeleteModal: React.FC<DeleteModalProps> = ({ handleDeleteModal }) => { 9 return ( 10 <ModalWrapper onClose={handleDeleteModal}> 11 <div className="mx-auto max-w-72"> 12 <p className="text-center text-xl font-extrabold leading-relaxed text-black"> 13 Are you sure you want to delete this task? 14 </p> 15 <div className="mt-6 flex justify-center gap-6"> 16 <button 17 className="rounded-xl bg-[#713FFF] px-7 py-2 text-base font-semibold text-white shadow-lg" 18 onClick={handleDeleteModal} 19 > 20 Delete 21 </button> 22 <button 23 className="rounded-xl border border-[#d8e0f0] bg-white px-7 py-2 text-base font-normal text-[#7d8592] shadow-sm" 24 onClick={handleDeleteModal} 25 > 26 Cancel 27 </button> 28 </div> 29 </div> 30 </ModalWrapper> 31 ) 32} 33 34export default DeleteModal

How to Use in a Parent Component

Here’s an example of how to use the DeleteModal in your application:

We are showing a modal using an "Open Modal" button in the UI. This is only for the demonstration purposes.

app.tsx
1import React, { useState } from 'react' 2import Button from './components/Button' 3import DeleteModal from './components/Modal/DeleteModal' 4 5const App: React.FC = () => { 6 const [showModal, setShowModal] = useState(false) 7 8 const handleDeleteModal = () => { 9 setShowModal(!showModal) 10 } 11 12 return ( 13 <div className="flex h-[100vh] w-full items-center justify-center"> 14 <Button onClick={handleDeleteModal}>Delete Task</Button> 15 16 {showModal && <DeleteModal handleDeleteModal={handleDeleteModal} />} 17 </div> 18 ) 19} 20 21export default App

React Tailwind Image Modal

Image Modal Preview

Image modal UI Tailwind

Tailwind Image Modal Component Code

We created Image Modal component from ModalWrapper, so it has all the features.

image-modal.tsx
1import React from 'react' 2import ModalWrapper from './ModalWrapper' 3 4type ImageModalProps = { 5 image: string 6 alt: string 7 onClose: () => void 8} 9 10const ImageModal: React.FC<ImageModalProps> = ({ image, alt, onClose }) => { 11 return ( 12 <ModalWrapper onClose={onClose}> 13 <div className="flex flex-col items-center justify-center pt-1"> 14 <img 15 src={image} 16 alt={alt} 17 className="max-h-[500px] max-w-full rounded-xl object-cover" 18 /> 19 <p className="mt-2 text-lg text-gray-600">{alt}</p> 20 </div> 21 </ModalWrapper> 22 ) 23} 24 25export default ImageModal

How to Use Image Modal in Parent Component

app.tsx
1import React, { useState } from 'react' 2import Button from './components/Button' 3import ImageModal from './components/Modal/ImageModal' 4 5const App: React.FC = () => { 6 const [showModal, setShowModal] = useState(false) 7 8 const handleImageModal = () => { 9 setShowModal(!showModal) 10 } 11 12 return ( 13 <div> 14 <div className="flex h-[100vh] w-full items-center justify-center"> 15 <Button onClick={handleImageModal}>Open Image Modal</Button> 16 </div> 17 {showModal && ( 18 <ImageModal 19 image="https://plus.unsplash.com/premium_photo-1673984261110-d1d931e062c0?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" 20 alt="Project Cover" 21 onClose={handleImageModal} 22 /> 23 )} 24 </div> 25 ) 26} 27 28export default App

Did it help? Let me know via LinkedIn, I would happy to have your feedback.


Flexy UI Newsletter

Build better and faster UIs.Get the latest Tailwind UI components directly in your inbox. No spam!


Flexy UI Newsletter

Build better and faster UIs.Get the latest Tailwind UI components directly in your inbox. No spam!