DEV Community

Cover image for How to create an efficient Modal component in React using Hooks and Portals
CodeBucks
CodeBucks

Posted on

How to create an efficient Modal component in React using Hooks and Portals

Modal is a common UX element. A modal is a dialog box/popup window that is displayed on top of the current page. You must have used pop-ups and notifications for your website. For some people pop-ups are really annoying😫 if it doesn't behave as it should. It must have good UI/UX.

In this article we're going to create an efficient Modal component🤩 from scratch without using any library.

Demo Link🖤: https://react-reusable-components.vercel.app/

Our main goal is to create an efficient modal which,

  • Has a good layout
  • Doesn't break ui when we use overflow in parent component
  • Can render content Dynamically
  • Clean and Elegant animation
  • Looks good (good UI)
  • Have more control for User (like clicking outside can close modal) etc.

Let’s get started!

If you prefer video format then you can watch this video 📽👇

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 node-sass package. To install it do,

npm install node-sass

Let's create Basic Modal component

open App.js file.

clean🧹 unnecessary code.

Now create one button in the App.js file to open and close modal just like this 👇

<div className="App">
      <button>
        Modal
      </button>
</div>
Enter fullscreen mode Exit fullscreen mode

Create one state to open and close modal.

Write below 👇 code:

Line 6: 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.

Next create Modal.js file and Write below 👇 code:

const Modal = () => {
  return (
    <div>
      Modal
    </div>
  );
};

export default Modal;
Enter fullscreen mode Exit fullscreen mode

Now import it in the Modal in App.js file.

Line 17: Here we have imported Modal component. And passed
modal state as show in the props.

Now open Modal.js and write below code 👇

Line 3: Deconstruct Show from the props.

Line 7: We will display modal only when show state is true.

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 and close Button (You can use any icon for close button).
  • Main tag contains content of the modal.
  • Footer has 2 buttons one is submit and another is cancel.

Now when you press a button 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 this styling in the Modal.scss file.

This will give your modal a better look.

Line 21: By applying this backdrop-filter you can make it
look like frost-glass.

Let's add Close event in modal

In the App.js file pass toggle method as a props in the modal just like this 👇

<Modal show={modal} title="My Modal" close={Toggle}/>
Enter fullscreen mode Exit fullscreen mode

open Modal.js file and deconstruct close from the props.

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)

Let's use Portals to render Modal component

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 or z-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👇

ReactDOM.createPortal
(
element,
DOM node where you want to render this element
)

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 this 👇

<div id="modal" />
<div id="root" />
Enter fullscreen mode Exit fullscreen mode

Open Modal.js file and edit it just like this,

Line 1: Import ReactDom.

Line 6: After return create portal using ReactDom.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.

Let's make Modal content Dynamic:

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>
Enter fullscreen mode Exit fullscreen mode

Here we have passed title as props and modal content as the children.

Open Modal.js and write,

(Final Modal.js code)

Line 5: Deconstruct title and children from the props.

Line 17: Insert title in the curly braces.

Line 22: Render children using the curly braces.

Now if you want to add a little animation in the modal you can watch the video or you can go to the git repository and read📚 code.

If you have any question just ask in the comments😉

Thanks For Reading😄

Feel free to visit my youtube channel:

@CodeBucks

Discussion (9)

Collapse
luishvdiaz profile image
Luis Humberto Vazquez Diaz

Everything looks very good! But when I pass container parameter in ReactDOM.createPortal being this "document.getElementById ('root')", it alerts me with this message: "Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Element'. Type 'null' is not assignable to type 'Element'" and I know it can become null, but if I put" document.getElementsByTagName ('body') [0] "if it compiles, but that's not where I need the modal, some help?.

Collapse
codebucks profile image
CodeBucks Author

Where are you rendering your modal element? It should not the 'root'.

Collapse
luishvdiaz profile image
Luis Humberto Vazquez Diaz

I see, the component with id modal ... I use typescript, it keeps giving the same error message :(

Thread Thread
tripol profile image
Ekanem

1) You can place the document.getElementById ('root') in a variable and add a null check. For example:

const portalDiv = document.getElementById('root')!

2) Pass the portalDiv as the second parameter AFTER the </> closing fragment

Collapse
dastasoft profile image
dastasoft

Great article! I would suggest using modules for your css because the names are quite common and can easily be in conflict with other styles in your project, especially if you plan to export this as a component to others.

Collapse
codebucks profile image
CodeBucks Author

Thanks for this suggestion. I'll keep that in mind😄

Collapse
amandap profile image
Amanda • Edited on

This was such a helpful, well laid out instructional tool. And everything worked for me up until creating the portal. If I comment out the portal lines of code in Modal.js, it works for me (as shown below). Yet if I use them, I get an error that says "Target container is not a DOM element." And I'm trying to locate why but not able to yet. Please advise? I'm pasting my code below, and including a screenshot of the error message I am getting for review.

Modal.js:

import ReactDOM from 'react-dom';
import Close from '../assets/images/close.svg';

const Modal = ({ show, close, title, children }) => {
//return ReactDOM.createPortal(
return(
<>
...
{/document.getElementById("modal")/}
</>
);
};

export default Modal;

in App.js function:

const [ modal, setModal ] = useState(false)
const showModal = () => setModal(!modal)

in App.js return:

handleClick={showModal}
text="Show Modal"
dark
primary
color="troposphere" />

This is Modal content

dev-to-uploads.s3.amazonaws.com/up...

Collapse
codebucks profile image
CodeBucks Author • Edited on

Hey, sorry for late reply.
Have you added div with the id modal in html file?
Also You should use document.getElementById line after the </ >.
I'd suggest you to refer the portal part and it's syntax from react documentation.
Let me know if this still doesn't work and if you can share your code via gist or GitHub it will be more helpful.

Collapse
israel1116 profile image
israel1116

This was great! Thank you so much!