usePortal React hook

April 04, 2021

views

A simple React hook for creating portals for next.js!

The hook

src/hooks/usePortal.ts
import * as React from "react";
 
export function usePortal(id = "unknown") {
  // the ref that is going to be returned.
  const ref = React.useRef<HTMLDivElement>(null);
 
  React.useEffect(() => {
    // The element that will be created onMount and removed on unMount.
    let element: HTMLDivElement | null = null;
 
    if (!ref.current) {
      element = document.createElement("div");
 
      // You can give a it custom id.
      element.setAttribute("id", `Modal_Portal_${id}`);
 
      // Add the element to the "body", this can also be any other element.
      document.body.appendChild(element);
 
      ref.current = element;
    }
 
    return () => {
      // on unMount remove the element from the DOM
      ref.current = null;
      document.body.removeChild(element);
    };
  }, [id]);
 
  // return the ref so it can be used
  return ref.current;
}

Usage

src/App.ts
import * as React from "react";
import ReactDOM from "react-dom";
import { usePortal } from "hooks/usePortal"
 
export const MyCoolModal = () => {
  const [isOpen, setOpen] = React.useState(false);
  // use the ref and add a custom Id
  const portalRef = usePortal("my-cool-modal");
 
  return (
    <div>
      <button onClick={() => setOpen(true)}>Open modal</button>
 
      {isOpen
        ? // Use react-dom to create a portal
          portalRef &&
          ReactDOM.createPortal(
            <div style={{ width: "100%" }}>
              <h1>Hello from the modal!</h1>
 
              <button onClick={() => setOpen(false)}>Close modal</button>
            </div>,
            // set the portal element to the portalRef
            portalRef,
          )
        : null}
    </div>
  );
};

Try it

Try it out yourself here 🚀

npm/yarn

You can now also use this hook via npm/yarn by installing my npm package:

npm install @casper124578/useful

Later in your project

import { usePortal } from "@casper124578/useful/hooks/usePortal";