{"id":11903,"library":"react-useportal","title":"React usePortal Hook","description":"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.","status":"maintenance","version":"1.0.19","language":"javascript","source_language":"en","source_url":"https://github.com/alex-cory/react-useportal","tags":["javascript","react","hook","use","portal","react-hook","react-component","modal","lightbox"],"install":[{"cmd":"npm install react-useportal","lang":"bash","label":"npm"},{"cmd":"yarn add react-useportal","lang":"bash","label":"yarn"},{"cmd":"pnpm add react-useportal","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency required for any React application.","package":"react","optional":false},{"reason":"Peer dependency required for rendering React components to the DOM, especially for portals.","package":"react-dom","optional":false},{"reason":"Runtime dependency for handling Server-Side Rendering (SSR) logic.","package":"use-ssr","optional":false}],"imports":[{"note":"The primary hook `usePortal` is a default export, not a named export. It returns a `Portal` component along with other utilities.","wrong":"import { usePortal } from 'react-useportal'","symbol":"usePortal","correct":"import usePortal from 'react-useportal'"},{"note":"The `Portal` component is returned by the `usePortal` hook, not directly imported from the package.","wrong":"import { Portal } from 'react-useportal'","symbol":"Portal","correct":"const { Portal } = usePortal()"},{"note":"For TypeScript users, import types using `import type` to avoid bundling unnecessary code.","symbol":"UsePortalOptions","correct":"import type { UsePortalOptions } from 'react-useportal'"}],"quickstart":{"code":"import React from 'react';\nimport usePortal from 'react-useportal';\n\nfunction App() {\n  // Basic usage: Portals content to document.body by default\n  const { Portal: BodyPortal } = usePortal();\n\n  // Custom target: Portal content to a specific element\n  // Ensure 'portal-root' exists in your HTML (e.g., public/index.html)\n  const { Portal: CustomPortal } = usePortal({\n    bindTo: typeof document !== 'undefined' ? document.getElementById('portal-root') : null,\n    // Ensure `document` is available for SSR compatibility\n  });\n\n  // Stateful example with open/close controls\n  const { Portal: StatefulPortal, openPortal, closePortal, isOpen } = usePortal();\n\n  return (\n    <div>\n      <h1>React usePortal Examples</h1>\n      <p>This text is rendered in the main application DOM.</p>\n\n      <h2>Stateless Portal to document.body</h2>\n      <BodyPortal>\n        <p style={{ border: '2px solid blue', padding: '10px' }}>\n          This content is portaled to the end of document.body.\n        </p>\n      </BodyPortal>\n\n      <h2>Custom Target Portal</h2>\n      <div id=\"portal-root\" style={{ border: '2px dashed green', padding: '10px' }}>\n        <p>This is the custom target element (e.g., in public/index.html).</p>\n        <CustomPortal>\n          <p style={{ border: '2px solid purple', padding: '10px' }}>\n            This content is portaled into '#portal-root'.\n          </p>\n        </CustomPortal>\n      </div>\n\n      <h2>Stateful Portal (Modal Example)</h2>\n      <button onClick={openPortal}>Open Stateful Portal</button>\n      {isOpen && (\n        <StatefulPortal>\n          <div style={{\n            position: 'fixed',\n            top: '50%',\n            left: '50%',\n            transform: 'translate(-50%, -50%)',\n            backgroundColor: 'white',\n            padding: '20px',\n            border: '1px solid black',\n            zIndex: 1000,\n          }}>\n            <h3>Hello from Stateful Portal!</h3>\n            <p>You can control its visibility with buttons.</p>\n            <button onClick={closePortal}>Close Modal</button>\n          </div>\n        </StatefulPortal>\n      )}\n    </div>\n  );\n}\n\nexport default App;\n","lang":"typescript","description":"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."},"warnings":[{"fix":"Evaluate alternatives like `react-portal` (if more actively maintained) or a custom `ReactDOM.createPortal` implementation for long-term projects or those requiring bleeding-edge React features.","message":"The `react-useportal` package has not seen active development or maintenance in over three years. While functional for many use cases, it may not receive updates for compatibility with future React versions or address new bugs/security concerns.","severity":"gotcha","affected_versions":">=1.0.19"},{"fix":"Wrap `document` access with `typeof document !== 'undefined'` or similar checks to conditionally execute client-side code, as shown in the quickstart example. This prevents 'document is not defined' errors during SSR.","message":"When using the `bindTo` option with `document.getElementById` or similar DOM APIs, ensure proper checks for `document` availability, especially in Server-Side Rendering (SSR) environments. Direct access to `document` on the server will cause errors.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Consider lazy initialization of portals or creating a single shared portal instance that dynamically updates its content for scenarios with a large number of potential portal triggers. The `programmaticallyOpen` option might also be useful to delay portal creation until needed.","message":"When creating many `usePortal` instances, especially for elements like tooltips that might appear on hover, there can be performance overhead due to `ReactDOM.createPortal()` being called immediately for each instance. This can lead to noticeable lag if not managed.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Manually implement WAI-ARIA authoring practices for interactive portal-based components (e.g., modals, tooltips), including focus management (trapping focus, restoring focus), keyboard navigation (Esc key to close), and appropriate ARIA roles and attributes.","message":"Portals, including those created with `react-useportal`, do not automatically handle accessibility concerns such as focus trapping, keyboard navigation, or ARIA attributes for modal dialogs. Implementing these correctly is crucial for an inclusive user experience.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Guard `document` access with `typeof document !== 'undefined' ? document.getElementById('my-id') : null` to ensure code runs only in a browser environment.","cause":"Attempting to access the `document` object during server-side rendering (SSR) without conditional checks, typically when using `bindTo: document.getElementById('...')`.","error":"ReferenceError: document is not defined"},{"fix":"Ensure the target DOM element exists before the portal tries to mount. For dynamically rendered targets, use a `ref` or `useEffect` to ensure the element is ready. For static targets, verify the ID matches an element in your `index.html`.","cause":"The DOM element specified in the `bindTo` option either does not exist, or the component attempting to create the portal mounts before the target element is available in the DOM.","error":"Error: Target container is not a DOM element."},{"fix":"Use `event.stopPropagation()` on event handlers within the portal's content or on its overlay to prevent events from bubbling up to undesired parent components in the React tree.","cause":"React's event system processes events through the React component tree, regardless of the physical DOM placement of portals. This can be counter-intuitive for elements visually outside their parent.","error":"Events on elements inside the Portal bubble to parent components in the React tree, not the DOM tree."}],"ecosystem":"npm"}