CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-portal

React library that simplifies working with React Portals by providing Portal and PortalWithState components for breaking out of normal DOM hierarchy.

Pending
Overview
Eval results
Files

index.mddocs/

React Portal

React Portal is a React library that simplifies working with React Portals by providing two main components: Portal and PortalWithState. The Portal component transports children into a new React Portal (typically appended to document.body) and supports targeting custom DOM elements, server-side rendering, and returning arrays without wrapper divs. The PortalWithState component adds convenience by managing its own state and providing features like close-on-ESC, close-on-outside-click, and callback functions for open/close events.

Package Information

  • Package Name: react-portal
  • Package Type: npm
  • Language: JavaScript (React)
  • Installation: npm install react-portal or yarn add react-portal

Core Imports

import { Portal, PortalWithState } from 'react-portal';

For CommonJS:

const { Portal, PortalWithState } = require('react-portal');

Basic Usage

import React from 'react';
import { Portal, PortalWithState } from 'react-portal';

// Basic Portal usage
const MyModal = ({ isOpen, children }) => (
  isOpen && <Portal>{children}</Portal>
);

// PortalWithState usage with built-in controls
const AdvancedModal = () => (
  <PortalWithState closeOnOutsideClick closeOnEsc>
    {({ openPortal, closePortal, isOpen, portal }) => (
      <React.Fragment>
        <button onClick={openPortal}>Open Modal</button>
        {portal(
          <div style={{ background: 'white', padding: '20px' }}>
            <h2>Modal Content</h2>
            <button onClick={closePortal}>Close</button>
          </div>
        )}
      </React.Fragment>
    )}
  </PortalWithState>
);

Architecture

React Portal is built around React's Portal API with the following key components:

  • Portal Component: Core portal functionality with automatic React version compatibility (16+ vs 15)
  • PortalWithState Component: Stateful wrapper providing built-in open/close management
  • Compatibility Layer: Automatic fallback between modern ReactDOM.createPortal and legacy unstable_renderSubtreeIntoContainer
  • Event Management: Built-in ESC key and outside click handling for modals and popups
  • SSR Support: Server-side rendering compatibility through DOM environment detection

Capabilities

Basic Portal Creation

Core portal component that transports React elements into a React Portal, breaking out of normal DOM hierarchy for proper styling and positioning.

/**
 * Portal component that renders children into a React Portal
 */
class Portal extends React.Component<PortalProps> {}

interface PortalProps {
  /** React elements to be portaled */
  children: React.ReactNode;
  /** Optional target DOM element (defaults to auto-created div in document.body) */
  node?: HTMLElement | Element | null;
}

Usage Examples:

// Portal to document.body (default)
<Portal>
  <div>This content appears at the end of document.body</div>
</Portal>

// Portal to custom element
const customTarget = document.getElementById('modal-root');
<Portal node={customTarget}>
  <div>This content appears in the custom element</div>
</Portal>

// Conditional portal
{isModalOpen && (
  <Portal>
    <div className="modal-overlay">
      <div className="modal-content">Modal content here</div>
    </div>
  </Portal>
)}

Stateful Portal Management

Advanced portal component with built-in state management, event handling, and render prop pattern for complete portal control.

/**
 * Stateful portal component using render prop pattern
 */
class PortalWithState extends React.Component<PortalWithStateProps> {}

interface PortalWithStateProps {
  /** Render prop function receiving portal controls */
  children: (controls: PortalControls) => React.ReactNode;
  /** Initial open state (default: false) */
  defaultOpen?: boolean;
  /** Target DOM element for portal */
  node?: HTMLElement | Element | null;
  /** Close portal when ESC key is pressed */
  closeOnEsc?: boolean;
  /** Close portal when clicking outside */
  closeOnOutsideClick?: boolean;
  /** Callback fired when portal opens (default: empty function) */
  onOpen?: () => void;
  /** Callback fired when portal closes (default: empty function) */
  onClose?: () => void;
}

interface PortalControls {
  /** Function to open the portal */
  openPortal: (event?: React.SyntheticEvent) => void;
  /** Function to close the portal */
  closePortal: () => void;
  /** Function that wraps content to be portaled */
  portal: (children: React.ReactNode) => React.ReactNode;
  /** Current open/closed state */
  isOpen: boolean;
}

Usage Examples:

// Basic modal with ESC and outside click handling
<PortalWithState closeOnOutsideClick closeOnEsc>
  {({ openPortal, closePortal, isOpen, portal }) => (
    <React.Fragment>
      <button onClick={openPortal}>Open Modal</button>
      {portal(
        <div className="modal-backdrop">
          <div className="modal">
            <h2>Modal Title</h2>
            <p>Modal content goes here</p>
            <button onClick={closePortal}>Close</button>
          </div>
        </div>
      )}
    </React.Fragment>
  )}
</PortalWithState>

// Modal with callbacks and custom target
<PortalWithState 
  closeOnEsc 
  onOpen={() => console.log('Modal opened')}
  onClose={() => console.log('Modal closed')}
  node={document.getElementById('overlay-root')}
>
  {({ openPortal, closePortal, isOpen, portal }) => (
    <div>
      <button onClick={openPortal}>
        {isOpen ? 'Close' : 'Open'} Lightbox
      </button>
      {portal(
        <div className="lightbox">
          <img src="/image.jpg" alt="Large image" />
          <button onClick={closePortal}>×</button>
        </div>
      )}
    </div>
  )}
</PortalWithState>

// Loading overlay that starts open
<PortalWithState defaultOpen>
  {({ closePortal, portal }) => 
    portal(
      <div className="loading-overlay">
        <div className="spinner">Loading...</div>
        <button onClick={closePortal}>Cancel</button>
      </div>
    )
  }
</PortalWithState>

React Version Compatibility

React Portal automatically handles React version compatibility:

  • React 16+: Uses ReactDOM.createPortal() (modern approach)
  • React 15: Falls back to ReactDOM.unstable_renderSubtreeIntoContainer() (legacy approach)
  • SSR: Detects server environment and renders null safely

Error Handling

React Portal handles cleanup automatically:

  • DOM nodes created by Portal are removed on unmount
  • Event listeners are properly cleaned up when PortalWithState unmounts
  • No memory leaks or orphaned DOM elements

Common Use Cases

  • Modals and Dialogs: Break out of parent container z-index stacking
  • Tooltips and Popovers: Position relative to viewport instead of parent
  • Loading Overlays: Cover entire page regardless of component location
  • Notifications: Render at consistent location in DOM tree
  • Lightboxes: Full-screen overlays independent of component hierarchy

Install with Tessl CLI

npx tessl i tessl/npm-react-portal

docs

index.md

tile.json