Creating an Efficient React Modal Component with Hooks and Portals
In this walkthrough, you’ll craft a top-notch React modal component🎨 without relying on external libraries.
Demo Link🖤: React Modal using Portals
Here’s a rundown of what we’ll cover:
Our main goal is to create an efficient modal which,
- Crafting a visually pleasing modal layout
- Ensuring the modal doesn’t disrupt the UI, especially with parent component overflow
- Introducing dynamic content rendering capabilities
- Incorporating sleek animations
- Implementing intuitive user controls, like outside-click to close
If you’re more of a visual learner, check out the complementary video tutorial📽👇:
Let’s get started!
Setting Up The Project
Create your React App using,
npx create-react-app react-modal
For this tutorial i’m going to use Sass
so make sure you have installed sass
package. To install it do,
npm install sass
Modal basic structure
Clean up🧹 the App.js
of any extra code and introduce a button to trigger the modal, as demonstrated below:
<div className="App">
<button>Modal</button>
</div>
To manage the modal’s open and close state, let’s use the useState
hook 👇:
1import { useState } from "react";
2import "./App.scss";
3
4function App() {
5
6 const [modal, setModal] = useState(false);
7 const Toggle = () => setModal(!modal);
8
9 return (
10 <div className="App">
11 <button className="clickme" onClick={Toggle}>
12 Modal
13 </button>
14 </div>
15 );
16}
17
18export default App;
19
Line 6: This line contains modal
state which is false initially.
Line 7: A Toggle
method to toggle modal
state from false to
true and vice-versa.
Line 11: Make sure to connect Toggle()
method to onClick
of the button.
Proceed to create the Modal.js
file, and start shaping the modal component:
const Modal = () => {
return (
<div>
Modal
</div>
);
};
export default Modal;
Integrate the Modal
component into App.js
and pass the modal state as props:
1import { useState } from "react";
2import "./App.scss";
3import Modal from "./Components/Modal";
4
5function App() {
6
7 const [modal, setModal] = useState(false);
8 const Toggle = () => setModal(!modal);
9
10 return (
11 <div className="App">
12 <button className="clickme" onClick={Toggle}>
13 Modal
14 </button>
15 <Modal show={modal} />
16 </div>
17 );
18}
19
20export default App;
21
Line 17: Here we have imported Modal
component. And passed modal
state as show
in the props.
Update Modal.js
with the following structure and functionalities👇:
1import Close from "./times-solid.svg";
2
3const Modal = ({ show }) => {
4 return (
5 <>
6 <div
7 className={`modalContainer ${show ? "show" : ""} `}
8 >
9 <div className="modal" >
10 <header className="modal_header">
11 <h2 className="modal_header-title"> Modal Title </h2>
12 <button className="close" >
13 <img src={Close} alt="close" />
14 </button>
15 </header>
16 <main className="modal_content">
17 This is Modal Content
18 </main>
19 <footer className="modal_footer">
20 <button className="modal-close" >
21 Cancel
22 </button>
23
24 <button className="submit">Submit</button>
25 </footer>
26 </div>
27 </div>
28 </>
29 );
30};
31
32export default Modal;
33
Line 3: Deconstruct show
from the props.
Line 7: We will display modal only when show
state is true so when the show state is true we will add the show
class.
Line 9 to 30: This is the whole Modal layout
.
ModalContainer
div contains the modal- In the modal div, There is one header which contains
modal title
andclose
Button (You can use any icon for close button). Main
tag containscontent
of the modal.- Footer has 2 buttons one is
submit
and another iscancel
.
Styling The Modal
Now when you press a button, the modal will show and on pressing again it will close the modal. First Let’s add some styling to our modal. Create Modal.scss
file and import it in the Modal.js
file. Copy and paste the following styles in the Modal.scss
file.
.modalContainer {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba($color: #000000, $alpha: 0.35);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
.modal {
width: 30vw;
height: auto;
background-color: #fff;
padding: 2rem;
border-radius: 20px;
background-color: rgba(255, 255, 255, 0.35);
backdrop-filter: blur(5px);
box-shadow: 0 0 1rem 0 rgba(0, 0, 0, 0.2);
&_header {
position: relative;
border-bottom: 1px solid #dddddd;
&-title {
text-align: center;
}
.close {
position: absolute;
top: 0;
right: 0;
background: transparent;
img {
width: 1rem;
height: auto;
transition: all 0.3s;
}
&:hover {
img {
transform: scale(1.1);
}
}
}
}
&_content {
border-bottom: 1px solid #dddddd;
padding: 2rem 0;
}
&_footer {
padding: 2rem 0;
padding-bottom: 0;
button {
float: right;
padding: 0.5rem;
border-radius: 8px;
}
.modal-close {
background-color: transparent;
font-weight: 600;
&:hover {
color: rgba(54, 67, 72, 0.8);
}
}
.submit {
margin-right: 1rem;
background-color: #364348;
color: #fff;
&:hover {
background-color: rgba(54, 67, 72, 0.8);
}
}
}
}
}
.modalContainer.show {
visibility: visible;
opacity: 1;
.modal {
transform: translateY(0);
}
}
This will give your modal a better look. Also, by applying the backdrop-filter
you can make it look like frost-glass. First we have kept opacity to 0 and visibility to hidden then in the last part we have added one class called show so when the class is enabled it will change the visibility and opacity values.
Adding a Close Event in Modal
In the App.js
file pass toggle
method as a props in the modal just like the following line 👇
<Modal show={modal} title="My Modal" close={Toggle} />
open Modal.js
file and deconstruct close
from the props.
1import Close from "./times-solid.svg";
2
3const Modal = ({ show, close }) => {
4 return (
5 <>
6 {
7 show ?
8
9 <div
10 className="modalContainer"
11 onClick={() => close()}
12 >
13 <div className="modal" onClick={(e) => e.stopPropagation()}>
14 <header className="modal_header">
15 <h2 className="modal_header-title">Modal Title</h2>
16 <button className="close" onClick={() => close()}>
17 <img src={Close} alt="close" />
18 </button>
19 </header>
20 <main className="modal_content">This is modal content</main>
21 <footer className="modal_footer">
22 <button className="modal-close" onClick={() => close()}>
23 Cancel
24 </button>
25
26 <button className="submit">Submit</button>
27 </footer>
28 </div>
29 </div>
30 : null
31 }
32 </>
33 );
34};
35
36export default Modal;
37
Line 3: Deconstruct close
from the props.
We have added close method in 3 places:
Line 16: At the close
button.
Line 22: At the cancel
button.
Line 11: We have also added close
method here too. Why? because whenever user clicks outside it should close the modal. So here, when user clicks on the modalContainer
it closes the modal.
Line 13: here we have to stop this click
events in the modal else it will close it so for that we have used e.stopPropagation()
.
hint: You can also add event Listener and add functionality in which when user clicks esc key, It closes the modal. (It is good for user experience)
Using React Portals to render the Modal
What🧐 is portals ?
- Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
Why🤔 should we use portals?
- Sometimes when we use
overflow
orz-index
properties in the parent component then we need child component (like modal or dialogues) to break container visually, and portals can be very handy to do that because it renders children outside of DOM hierarchy.
Syntax✍ for to create portal👇
createPortal(children, DOM node - where you want to render this element, key?-A unique string or number to be used as the portal’s key)
So let’s implement portals in our Modal
component. To use portal
we have to create one more element in the dom. Generally our whole App
renders in the div with the id root.
Open index.html
file and above the root div
create one more div
with the id modal.
Just like the following code block👇
<div id="modal" />
<div id="root" />
Let’s update the ‘Modal.js’ file.
1import { createPortal } from "react-dom";
2import "./modal.scss";
3import Close from "../times-solid.svg";
4
5const Modal = ({ show, close }) => {
6 return createPortal(
7 <>
8 <div
9 className={`modalContainer ${show ? "show" : ""} `}
10 onClick={() => close()}
11 >
12 <div className="modal" onClick={(e) => e.stopPropagation()}>
13 <header className="modal_header">
14 <h2 className="modal_header-title">{title}</h2>
15 <button className="close" onClick={() => close()}>
16 <img src={Close} alt="close" />
17 </button>
18 </header>
19 <main className="modal_content">{children}</main>
20 <footer className="modal_footer">
21 <button className="modal-close" onClick={() => close()}>
22 Cancel
23 </button>
24 <button className="submit">Submit</button>
25 </footer>
26 </div>
27 </div>
28 </>,
29 document.getElementById("modal")
30 );
31};
32
33export default Modal;
34
Line 1: let’s import createPortal
from react-dom
.
Line 6: After return let’s create portal using createPortal
, As it’s first argument we have passed whole modal component and for the second argument we have passed the dom node where we want to render it.
Line 34: We want to render our component in the div
with id modal.
Adding dynamic content to the Modal
open App.js
file and pass title
as a props and content inside the component as shown below,
<Modal show={modal} title="My Modal" close={Toggle}>
This is Modal content
</Modal>
Here we have passed title as props and modal content as the children. Here is the final code for modal component.
1import "./modal.scss";
2import Close from "../times-solid.svg";
3import { createPortal } from "react-dom";
4
5const Modal = ({ show, close, title, children }) => {
6 return createPortal(
7 <>
8 <div
9 className={`modalContainer ${show ? "show" : ""} `}
10 onClick={() => close()}
11 >
12 <div className="modal" onClick={(e) => e.stopPropagation()}>
13 <header className="modal_header">
14 <h2 className="modal_header-title">{title}</h2>
15 <button className="close" onClick={() => close()}>
16 <img src={Close} alt="close" />
17 </button>
18 </header>
19 <main className="modal_content">{children}</main>
20 <footer className="modal_footer">
21 <button className="modal-close" onClick={() => close()}>
22 Cancel
23 </button>
24 <button className="submit">Submit</button>
25 </footer>
26 </div>
27 </div>
28 </>,
29 document.getElementById("modal")
30 );
31};
32
33export default Modal;
34
Line 5: Let’s deconstruct title
and children
from the props.
Line 17: Here, insert title in the curly braces.
Line 22: Let’s render the children using the curly braces.
Checkout the final code for this tutorial👉 create Modal using React Portals