I was working on a React project where I needed a modal component to confirm user actions. The issue is… React doesn’t come with a built-in modal. So, I had to create one from scratch.
In this article, I’ll show you two simple ways to create a modal component in React. The first method uses basic React state and props, and the second method uses React Portals for more advanced scenarios.
Both methods are easy to follow, and I’ll share complete code examples you can copy and run in your own projects.
Method 1 – Create a Simple Modal with State and Props
This is the easy way to build a modal. I use React’s useState hook to control the visibility of the modal.
Here’s the complete code:
import React, { useState } from "react";
import "./App.css";
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return (
<div className="modal-overlay">
<div className="modal-content">
{children}
<button className="close-btn" onClick={onClose}>
Close
</button>
</div>
</div>
);
}
export default function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div className="App">
<h1>React Modal Example</h1>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<h2>Confirm Your Action</h2>
<p>Are you sure you want to continue?</p>
</Modal>
</div>
);
}You can refer to the screenshot below to see the output.

- I first created a Modal component that only renders if
isOpenis true. - Then, I used
useStateinAppto toggle the modal’s visibility.
This method works great for simple use cases like confirmation dialogs.
Method 2 – Create a Modal with React Portals
Sometimes, you don’t want your modal to be nested inside the main component tree. For example, if you have complex layouts, it’s better to render modals at the root level.
React Portals make this possible.
Here’s the complete code:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./App.css";
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return ReactDOM.createPortal(
<div className="modal-overlay">
<div className="modal-content">
{children}
<button className="close-btn" onClick={onClose}>
Close
</button>
</div>
</div>,
document.getElementById("modal-root")
);
}
export default function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div className="App">
<h1>React Modal with Portal</h1>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<h2>U.S. Example – Confirm Deletion</h2>
<p>Do you really want to delete this customer record?</p>
</Modal>
</div>
);
}You can refer to the screenshot below to see the output.

- I used ReactDOM.createPortal to render the modal into a separate DOM node (
modal-root). - This ensures the modal stays on top of other UI elements without breaking the layout.
For this to work, add a div with id modal-root in your public/index.html:
<body>
<div id="root"></div>
<div id="modal-root"></div>
</body>This method is my go-to for production apps because it’s flexible and avoids layout issues.
Method 3 – Reusable Modal Component with Props
If you need modals in multiple places, it’s better to create a reusable component that accepts props like title, message, and onConfirm.
Here’s the complete code:
import React, { useState } from "react";
import "./App.css";
function ReusableModal({ isOpen, title, message, onClose, onConfirm }) {
if (!isOpen) return null;
return (
<div className="modal-overlay">
<div className="modal-content">
<h2>{title}</h2>
<p>{message}</p>
<div className="modal-actions">
<button onClick={onConfirm}>Confirm</button>
<button onClick={onClose}>Cancel</button>
</div>
</div>
</div>
);
}
export default function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
const handleConfirm = () => {
alert("Action confirmed!");
setIsModalOpen(false);
};
return (
<div className="App">
<h1>Reusable Modal Example</h1>
<button onClick={() => setIsModalOpen(true)}>Delete Record</button>
<ReusableModal
isOpen={isModalOpen}
title="Confirm Deletion"
message="Do you really want to delete this U.S. customer record?"
onClose={() => setIsModalOpen(false)}
onConfirm={handleConfirm}
/>
</div>
);
}You can refer to the screenshot below to see the output.

- I created a ReusableModal that accepts props for title, message, and actions.
- This makes it easy to reuse the modal across different parts of the app.
Add Basic CSS for the Modal
Here’s a simple CSS you can add in App.css to make the modal look clean:
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 400px;
max-width: 90%;
}
.close-btn,
.modal-actions button {
margin-top: 15px;
margin-right: 10px;
padding: 8px 12px;
border: none;
background: #007bff;
color: white;
border-radius: 4px;
cursor: pointer;
}
.close-btn:hover,
.modal-actions button:hover {
background: #0056b3;
}So while React doesn’t provide a built-in modal, we can easily create one using state and props, or take it further with React Portals.
I personally prefer the portal-based approach for production apps, but the simple state-based method works fine for quick prototypes.
Both methods are reusable, and once you’ve set up a modal component, you can use it across your entire project.
Building a React Modal Component Example
You may read:
- Build an Infinite Scroll Component in React
- What is a React Component?
- React Component Naming Conventions
- Top React UI Component Libraries to Build Modern Web Apps

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.