React usePortal Hook

1.0.19 · maintenance · verified Sun Apr 19

react-useportal is a utility React hook for creating and managing React Portals, a feature introduced in React 16 that allows components to render children into a DOM node that exists outside the DOM hierarchy of the parent component. Currently at version 1.0.19, the package simplifies common use cases such as modals, tooltips, dropdowns, and notifications. Key differentiators include built-in isomorphic Server-Side Rendering (SSR) support, full TypeScript compatibility, and a minimal dependency footprint (primarily `use-ssr`). Despite its utility, the project has seen very limited maintenance and updates in recent years, making its release cadence effectively dormant. It aims to abstract away the direct usage of `ReactDOM.createPortal` for a more React-hook-centric API.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates basic stateless portal usage to `document.body` and a custom target, alongside a stateful example showing how to programmatically open and close a modal-like portal. It includes SSR guards for `document` access.

import React from 'react';
import usePortal from 'react-useportal';

function App() {
  // Basic usage: Portals content to document.body by default
  const { Portal: BodyPortal } = usePortal();

  // Custom target: Portal content to a specific element
  // Ensure 'portal-root' exists in your HTML (e.g., public/index.html)
  const { Portal: CustomPortal } = usePortal({
    bindTo: typeof document !== 'undefined' ? document.getElementById('portal-root') : null,
    // Ensure `document` is available for SSR compatibility
  });

  // Stateful example with open/close controls
  const { Portal: StatefulPortal, openPortal, closePortal, isOpen } = usePortal();

  return (
    <div>
      <h1>React usePortal Examples</h1>
      <p>This text is rendered in the main application DOM.</p>

      <h2>Stateless Portal to document.body</h2>
      <BodyPortal>
        <p style={{ border: '2px solid blue', padding: '10px' }}>
          This content is portaled to the end of document.body.
        </p>
      </BodyPortal>

      <h2>Custom Target Portal</h2>
      <div id="portal-root" style={{ border: '2px dashed green', padding: '10px' }}>
        <p>This is the custom target element (e.g., in public/index.html).</p>
        <CustomPortal>
          <p style={{ border: '2px solid purple', padding: '10px' }}>
            This content is portaled into '#portal-root'.
          </p>
        </CustomPortal>
      </div>

      <h2>Stateful Portal (Modal Example)</h2>
      <button onClick={openPortal}>Open Stateful Portal</button>
      {isOpen && (
        <StatefulPortal>
          <div style={{
            position: 'fixed',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            backgroundColor: 'white',
            padding: '20px',
            border: '1px solid black',
            zIndex: 1000,
          }}>
            <h3>Hello from Stateful Portal!</h3>
            <p>You can control its visibility with buttons.</p>
            <button onClick={closePortal}>Close Modal</button>
          </div>
        </StatefulPortal>
      )}
    </div>
  );
}

export default App;

view raw JSON →