How to create reusable and customizable modals with React and TypeScript
Tags: react, typescript, webdev, programming
Published: 2022-12-18 02:14:58

As a frontend developer, you have probably had to build a modal window on more than one occasion. This type of element differs from the typical pop-ups because it does not appear automatically, but it is the user who has to click somewhere on the website (usually a button) to make it appear.

In this tutorial, you will learn how to develop and implement a modal component in your React project with TypeScript. It will be reusable in any part of your application, and you will be able to customize it and add any type of content.

## What Are Modal Windows?
Modals are undoubtedly one of the most used components on the web because they can be used in different contexts, from messages to user input. They have placed an overlay on the screen. Therefore, they have visual precedence over all other elements.

Like many other components in React, a dependency can be installed to help in this process. However, we always end up limited in several aspects, and one of them is styling.

We can create a modal inside or outside the element we call it from in the DOM hierarchy, but to fulfill the definition of a modal, it should be at the same level as the element used as root in React, and to achieve this, we will use the Portals.

## What Are Portals in React?
Portals provide a quick and easy way to render children to a DOM node that exists outside the DOM hierarchy of the parent component.

In React, the default behavior is to render the entire application under a single DOM node — the root of the application, but what if we want to render children outside the root DOM node? And you want children to appear visually on top of its container.

A Portal can be created using `ReactDOM.createPortal(child, container)`. Here the child is a React element, fragment, or string, and the container is the DOM location (node) to which the portal should be injected.

Below is an example of a modal component created using the above API.

{% embed https://gist.github.com/ElectricEgghead/8df1ff9b2204c4ee5288c38bec4d1af5 %}

Although a Portal is rendered outside the parent DOM element, it behaves similarly to a normal React component within the application. It can access props and the context API.

This is because the Portals reside within the React Tree hierarchy, and Portals only affect the HTML DOM structure and do not impact the React component tree.

## Developing Modals in React
Setting up
We create our application with vite with the following command:

`yarn create vite my-modals-app --template react-ts`

We install the dependencies that we will need in the project:

`yarn add styled-components @types/styled-components`

After that, we create the following structure for the project:

```bash
src/
├── components/
│   ├── layout/
│   │   ├── Header.tsx
│   │   └── styles.tsx
│   ├── modals/
│   │   ├── Buttons.tsx
│   │   ├── Modal.tsx
│   │   ├── PortalModal.tsx
│   │   ├── index.ts
│   └── └── styles.ts
├── hooks/
│   └── useOnClickOutside.tsx
├── styles/
│   ├── modal.css
│   ├── normalize.css
│   └── theme.ts
├── ts/
│   ├── interfaces/
│   │   └── modal.interface.ts
│   ├── types/
│   └── └── styled.d.ts
├── App.tsx
├── main.tsx
└── config-dummy.ts
```

## Components
As we can see in the folder structure, we have several functional and styling components for this app, but in order not to make this tutorial long, we will focus only on the main components.

`App.tsx`: In this component, we have examples of how to use our custom modal. We have buttons that show modals with different configurations to give us an idea of what we can achieve with this modal.

In this component, we also define the theme for our modal, adding a `ThemeProvider` and creating a global style with `createGlobalStyle` of `styled-components`.

{% embed https://gist.github.com/ljaviertovar/ba6d9ed8a50d367fbf73fa71b9cc70a8 %}

`Modal.tsx`: This component is conditioned to be displayed or not depending on the action performed by the user. It is wrapped in a style component that is superimposed on the screen.

This component receives as property the configuration that is where we will define how our modal will be seen, that is to say, the position where it will be shown, the title of the modal, paddings, etc.

It also receives children, which contains all the content that will be shown inside the modal. It can be any type of `tsx` content.

Also, in this component, we have a couple of functionalities, which serve us to close the modal.

`useOnClickOutside`: This is a custom hook that will close the modal when it detects that the user clicks outside the modal.

This hook receives as a parameter the reference of the element that we want to detect and a callback that is the action that we want to make when detecting a click.

This hook adds an `EventListener` that will respond to the `mousedown` and touchstartevent, after this, it will evaluate if the click was inside the element or outside of it.

handleKeyPress: This is a callback that will be executed when it detects that the user presses the ESC key to close the modal.

It does this by adding an `EventListener` to the `keydownevent` to then evaluate which key was pressed.

{% embed https://gist.github.com/ljaviertovar/b93cc3379678e1a594bac83927dffa10 %}

`PortalModal.tsx`: This component uses the React Portals, which we have already mentioned previously.

It receives children that would be our modal and an id that we will use to assign it to an HTML element.

In this component, we use the hook `useLayoutEffect`. This hook is a little different from useEffect since this one is executed when it detects a change in the virtual DOM and not in the state, which is exactly what we are doing when creating a new element in the DOM.

Inside the `useLayoutEffect`, we look for and validate if the element has already been created with the id that we have passed, and we set this element. Otherwise, we make a new element in the DOM with the function `createWrapperAndAppenToBody`.

With this function, we can create the element where it best suits us. In this case, it is being created at the same level as the root element within the body.

Once we have created the element where we are going to insert our modal, we create the portal with `createPortal`.

{% embed https://gist.github.com/ljaviertovar/30a1f304dd406ee29f7713d6e3f17c9f %}

`configDummy.ts`: This is the file we will use as a template to generate different modals, in this case, 4.

As you can see, you can make a lot of combinations to generate a modal different from each other, and you could add more configurations if you wish.

{% embed https://gist.github.com/ljaviertovar/baf1189f650574a29a2da8936082da3b %}

That’s it! we have our cool Modals.

[See the demo here.](https://resusable-customizable-modals.netlify.app/)

[Repo here.](https://github.com/ljaviertovar/reusable-modals-react-ts)

## Conclusion
In this tutorial, we have created a reusable component as we can use it anywhere in our application. Using React Portals, we can insert it anywhere in the DOM as it will create a new element with the id, we assign to it.

We also have different styling options for our modal, and we can add the ones we can think of, besides having implemented a dark mode that I particularly like.

I hope this tutorial has been useful for you and that you have learned new things in developing this application.

---

**Read more:**

- [How to build an autocomplete search component in React and TypeScript](https://dev.to/ljaviertovar/autocomplete-search-component-with-react-and-typescript-2lj3)

- [Why Should You Use Cleanup Functions in React’s useEffect Hook?](https://dev.to/ljaviertovar/why-should-you-use-cleanup-functions-in-reacts-useeffect-hook-2g2c)

Want to Connect?
Love connecting with friends all around the world on [Twitter](https://twitter.com/ljaviertovar).