React Portals let you render components outside the main DOM tree — perfect for modals, tooltips, and dropdowns. But managing dozens of portals manually gets messy fast. Here's how to create a clean Portal Manager system in React without external libraries.

Why Use a Portal Manager?

Common use cases:

  • Centralized control over all modals, tooltips, and popovers
  • Dynamic stacking, layering, and closing logic
  • Cleaner separation of UI layers

Step 1: Build a Portal Host

This component will be your centralized container for all portals:

// PortalHost.js
import { createContext, useContext, useState } from "react";
import { createPortal } from "react-dom";

const PortalContext = createContext(null);

export function PortalHost({ children }) {
  const [portals, setPortals] = useState([]);

  const mount = (node) => {
    const id = Math.random().toString(36).substr(2, 9);
    setPortals((prev) => [...prev, { id, node }]);
    return id;
  };

  const unmount = (id) => {
    setPortals((prev) => prev.filter((p) => p.id !== id));
  };

  return (
    
      {children}
      {portals.map(({ id, node }) =>
        createPortal(node, document.body)
      )}
    
  );
}

export function usePortal() {
  return useContext(PortalContext);
}

Step 2: Create a Hook for Dynamic Portals

We’ll build a simple hook to open and close portals:

// useDynamicPortal.js
import { usePortal } from "./PortalHost";
import { useEffect, useRef } from "react";

export function useDynamicPortal(content) {
  const { mount, unmount } = usePortal();
  const idRef = useRef(null);

  useEffect(() => {
    idRef.current = mount(content);
    return () => unmount(idRef.current);
  }, [content, mount, unmount]);
}

Step 3: Use It to Create a Modal Dynamically

Example of launching a modal when clicking a button:

// ExampleModal.js
import { useState } from "react";
import { useDynamicPortal } from "./useDynamicPortal";

function ModalContent({ onClose }) {
  return (
    

I'm a modal!

); } function ExampleModal() { const [open, setOpen] = useState(false); if (open) { useDynamicPortal( setOpen(false)} />); } return ; } export default ExampleModal;

Step 4: Wrap the App with the PortalHost

// App.js
import { PortalHost } from "./PortalHost";
import ExampleModal from "./ExampleModal";

function App() {
  return (
    
      
    
  );
}

export default App;

Pros and Cons

✅ Pros

  • Centralized portal management
  • No external libraries needed
  • Clean separation of modal logic

⚠️ Cons

  • More setup compared to a simple createPortal use
  • Limited layering control without manual z-indexing

🚀 Alternatives

  • React-Aria or Radix UI for more structured UI primitives
  • Headless UI modals if you want battle-tested accessibility

Summary

When your React app grows, managing multiple popups, tooltips, and modals becomes chaotic. A Portal Manager brings clean order to the chaos, and this simple implementation is a solid foundation to customize even further for complex use cases.

For a much more extensive guide on getting the most out of React portals, check out my full 24-page PDF file on Gumroad. It's available for just $10:

Using React Portals Like a Pro.

If you found this useful, you can support me here: buymeacoffee.com/hexshift