React-cool-portal: What it is and how to use it
Tags: react
Published: 2020-05-27 14:23:01

---
title: React-cool-portal: What it is and how to use it
published: true
date: 2020-05-08 17:30:55 UTC
tags: react
canonical_url: https://blog.logrocket.com/react-cool-portal-what-it-is-and-how-to-use-it/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/03aq7coy6mx2kkwpauug.jpeg
---
**Written by [Yusuff Faruq](https://blog.logrocket.com/author/yusufffaruq/)**✏️

If you’re familiar with React, you are most likely already aware of the fact that React renders all the HTML elements under a single `div` tag, often given an ID of `root`.

```jsx
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
```

This can be annoyingly restrictive if you are trying to render another element, such as a modal or a tooltip, outside the root element. In a bid to solve this problem, React portals were introduced.

## Introducing React portals

In version 16.0 of React, portals were introduced to solve the inability to render other elements outside the root node.

Here is an example from the React docs on how to make use of React portals:

```jsx
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }
  componentDidMount() {
    modalRoot.appendChild(this.el);
  }
  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks: 0};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // This will fire when the button in Child is clicked,
    // updating Parent's state, even though button
    // is not direct descendant in the DOM.
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        <p>Number of clicks: {this.state.clicks}</p>
        <p>
          Open up the browser DevTools
          to observe that the button
          is not a child of the div
          with the onClick handler.
        </p>
        <Modal>
          <Child />
        </Modal>
      </div>
    );
  }
}

function Child() {
  // The click event on this button will bubble up to parent,
  // because there is no 'onClick' attribute defined
  return (
    <div className="modal">
      <button>Click</button>
    </div>
  );
}

ReactDOM.render(<Parent />, appRoot);
```

As you can see, the code is a little verbose and could be more readable and understandable if its length were reduced. Fast-forward to the introduction of Hooks in React version 16.8.

Hooks allow developers to reuse stateful logic without changing component hierarchy. They have changed the way React developers write code and compose state. In fact, many React libraries, such as react-redux and react-router, are moving — or have already moved — towards a more Hooks-based API.

Many new Hooks-based libraries are also being released to provide some abstraction over certain services or code. One such library is react-cool-portal. In this article, we will explore react-cool-portal, and by the end, we will have built an animated modal. Let’s get started!

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## What is react-cool-portal?

Like I mentioned earlier, react-cool-portal is a Hooks-based abstraction over React portals. With react-cool-portal, creating a portal is as simple as:

```jsx
const {Portal} = usePortal();
```

The Hook also comes with various methods and event listeners that allow you to perform more flexible operations. Here are some of the features of react-cool-portal:

- You have the ability to render an element/component to a default element in the `<body>` or to a specified DOM element
- react-cool-portal comes with various state controllers and event listeners that allow you to handle your portals more flexibly
- It can be used as a wrapper to build your custom Hook
- It automatically removes unused portal containers for you, thereby preventing DOM mess
- It supports TypeScript type definitions
- It has server-side rendering compatibility
- It has a tiny size ([~ 1.4KB gzipped](https://bundlephobia.com/result?p=react-cool-portal)) with no external dependencies, aside from `react` and `react-dom`

In this article, we will explore react-cool-portal, and by the end, we will have built an animated modal. Let’s get started!

### Exploring react-cool-portal

As I mentioned earlier, you can create a portal with the `usePortal` Hook. `usePortal` returns an object that contains the `Portal` component and some functions:

```jsx
const {Portal, show, hide, isShow, toggle} = usePortal();
```

The `show` function is used to show the portal (mount it) while `hide` is used to hide it (unmount it). `isShow` is a Boolean, which returns `true` when the portal is mounted and `false` if otherwise. `toggle` is a function that can be used to show or hide the portal depending on the portal’s current state.

We can provide an argument in the form of an object to our `usePortal` Hook for further configuration, like so:

```jsx
const {Portal} = usePortal({
  defaultShow: false,
  internalShowHide: true,
  onShow : e => {
  },
  onHide: e => {
  },
  containerId: "portal",
  clickOutsideToHide: true,
  escToHide: true
});
```

With `defaultShow`, we can choose to show or hide our portal’s content by default. `internalShowHide` enables/disables the show/hide functions of our portal so we can handle the portal however we like.

`onShow` and `onHide` are event handlers that are triggered when `isShow` is set to `true` or when `isShow` is set to `false`, respectively. `containerId`, which has a default value of `react-cool-portal`, is used to set the ID of the portal.

`clickOutsideToHide` defines whether we want to hide the portal by clicking outside of it, while `escToHide` defines whether we want to hide the portal by clicking the `esc` key.

That’s basically all you need to know to start using react-cool-portal! Now we will build an animated modal with what we have learned so far.

## Building an animated modal

As with any React project, you have to create a new project before you start working. You can easily use create-react-app for that:

```jsx
npx create-react-app animated-modal
```

Once you have created a new project, you need to install react-cool-portal, like so:

```jsx
npm install --save react-cool-portal
```

Since this is a pretty small project, I will write all my JavaScript/JSX in one file, `App.js`, and all my CSS in another file, `App.css`.

Before we continue, let’s picture what we want to create. We want to have a button that, when clicked, will display an animated modal. We can then exit the modal from the modal itself.

That said, we need to create a piece of state that renders the `Portal` component depending on whether or not the button has been clicked:

```jsx
const [showModal, setShowModal] = useState(false);
```

We also need another piece of state to store the animation state (the different CSS classes that will trigger the animation):

```jsx
const [animationState, setAnimationState] = useState("");
```

We should have this currently:

```jsx
const { Portal, show, hide } = usePortal({
    defaultShow: false,
  });
  const [showModal, setShowModal] = useState(false);
  const [animationState, setAnimationState] = useState("");
```

Now let’s create our simple button element, which will set `showModal` to `true` when clicked and which will call the `show` function.

```jsx
return (
    <div>
      <button
        onClick={() => {
          setShowModal(true);
          show();
        }}
      >
        Open Modal
      </button>    
    </div>
  );
```

Now let’s write the modal markup:

```jsx
return (
    <div>
      <button
        onClick={() => {
          setShowModal(true);
          show();         
        }}
      >
        Open Modal
      </button>
      {showModal && (
        <Portal>
          <div className= "modal" tabIndex={-1}>
            <div
              className={`modal-dialog ${animationState}`}
              role="dialog"
              aria-labelledby="modal-label"
              aria-modal="true"
            >
              <div className="modal-header">
                <h5 id="modal-label">Modal header</h5>
                <span
                  className="modal-exit"
                >
                  close
                </span>
              </div>
              <div className="modal-body">
                <p>Modal Body</p>
              </div>
            </div>
          </div>
        </Portal>
      )}
    </div>
  );
```

In our CSS, we are going to have two different animations: `slideIn` and `slideOut`. When the button is clicked to open the modal, the animation state is changed to `slideIn`, and the `slideIn` class is attached to the modal dialog `div`.

To do this, we will use the `onShow` event handler to set the current animation state to `slideIn`. So anytime the modal is displayed, the `slideIn` class will be attached to it.

```jsx
const { Portal, show, hide } = usePortal({
    defaultShow: false,
    onShow: () => {
      setAnimationState("slideIn");
    },
  });
```

Our modal has a `<span>` element that will be used to close the modal when clicked. When this `<span>` element is clicked, we will set the animation state to `slideOut`.

```jsx
<span
  className="modal-exit"
  onClick={() => {
    setAnimationState("slideOut");
  }}
>
 close
</span>
```

We will now make use of one of the animation events that React provides: `onAnimationEnd`. The event handler passed to it will run once the animation has ended.

In our case, once the animation on the modal dialog has ended, we will check the current animation state. If it is `slideOut`, we will hide the modal. Once that’s done, we will set the animation state to an empty string.

```jsx
 <div
  className={`modal-dialog ${animationState}`}
   role="dialog"
   aria-labelledby="modal-label"
   aria-modal="true"
    onAnimationEnd={() => {
       if(animationState == "slideOut"){
          hide();
        }
        setAnimationState("");
    }}
 >
```

Our `App` component should now look like this:

```jsx
import React, { useState } from "react";
import "./App.css";
import usePortal from "react-cool-portal";

function App() {
  const { Portal, show, hide } = usePortal({
    defaultShow: false,
    onShow: () => {
      setAnimationState("slideIn");
    },
  });
  const [showModal, setShowModal] = useState(false);
  const [animationState, setAnimationState] = useState("");
  return (
    <div>
      <button
        onClick={() => {
          setShowModal(true);
          show(); 
        }}
      >
        Open Modal
      </button>
      {showModal && (
        <Portal>
          <div className= "modal" tabIndex={-1}>
            <div
              className={`modal-dialog ${animationState}`}
              role="dialog"
              aria-labelledby="modal-label"
              aria-modal="true"
              onAnimationEnd={() => {
                if(animationState == "slideOut"){
                  hide();
                }
                setAnimationState("");
              }}
            >
              <div className="modal-header">
                <h5 id="modal-label">Modal header</h5>
                <span
                  className="modal-exit"
                  onClick={() => {
                    setAnimationState("slideOut");
                  }}
                >
                  close
                </span>
              </div>
              <div className="modal-body">
                <p>Modal Body</p>
              </div>
            </div>
          </div>
        </Portal>
      )}
    </div>
  );
}
```

That’s it for the JavaScript — let’s move on to the the CSS. This is the CSS for the modal:

```jsx
body{
  --curve: cubic-bezier(0.22, 1, 0.36, 1);
}
#react-cool-portal{
  position: absolute;
  top:0;
  left: 0;
  min-width: 100vw;
  height: 100%;
}
.modal{
  height: 100%;
  width: 100%;
  display: flex;
  z-index: 20;
  justify-content: center;
  align-items: center;
  background-color: rgba(0,0,0,0.7);
}
.modal-dialog{
  background-color: white;
  border-radius: 10px;
  width: 80%;
  max-width: 400px;
  padding: 1rem;

}
.modal-header{
  font-weight: 400;
  font-size: 1.5rem;
  display: flex;
  justify-content: space-between;
}
.modal-header #modal-label{
  margin:0;
}
.modal-exit{
  font-size: 1rem;
  color: red;
  cursor: pointer;
}
.slideIn{
  animation: slideIn 0.5s var(--curve) 0s 1 normal none;
}
.slideOut{
  animation: slideOut 0.5s var(--curve) 0s 1 normal forwards;
}
@keyframes slideIn {
  0% {
    transform: translateY(-2rem);
    opacity: 0;
  }

  100% {
    transform: translateY(0);
    opacity: 1;
  }
}
@keyframes slideOut {
  100% {
    transform: translateY(-2rem);
    opacity: 0;
  }
  0% {
    transform: translateY(0);
    opacity: 1;
  }
}
```

You should now have a nicely animated modal!

## Conclusion

With that, we’re done! You can create a custom Hook called `useModal` based on react-cool-portal for code reusability.

The link to the repo for this project can be found [here](https://github.com/NodeJSs/react-cool-portal-article). You can find the live demo [here](https://react-cool-portal-article.netlify.app/). And, finally, you can learn more about react-cool-portal [here](https://github.com/wellyshen/react-cool-portal).

* * * 
## Full visibility into production React apps 

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, [try LogRocket.](https://www2.logrocket.com/react-performance-monitoring)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/eq752g8qhbffxt3hp9t4.png)

[LogRocket](https://www2.logrocket.com/react-performance-monitoring) is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — [start monitoring for free.](https://www2.logrocket.com/react-performance-monitoring)
* * *

The post [React-cool-portal: What it is and how to use it](https://blog.logrocket.com/react-cool-portal-what-it-is-and-how-to-use-it/) appeared first on [LogRocket Blog](https://blog.logrocket.com).