# How to Use a React Portal: Complete Guide with Examples

> Learn how to use React portals with ReactDOM createPortal. Render children outside the parent DOM hierarchy for modals and tooltips. Get the complete guide and examples.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: how-to-guide
- Published: 2026-02-15

---

**React portals let you render children into a DOM node that exists outside the parent component's DOM hierarchy using `ReactDOM.createPortal(children, container)`, enabling patterns like modals and tooltips while preserving React context and event bubbling.**

A react portal is a first-class feature in the facebook/react repository that solves layout constraints such as `overflow: hidden` or `z-index` stacking contexts. By mounting a subtree to a different physical DOM location, you maintain full React functionality—including state, context, and event delegation—without changing the component hierarchy.

## What Is a React Portal?

A react portal is a special React element that instructs the reconciler to mount its children into a DOM container that is not a descendant of the parent component’s DOM node. Internally, calling `ReactDOM.createPortal` returns a `ReactPortal` object tagged with the `HostPortal` work tag, as defined in [`packages/react-reconciler/src/ReactPortal.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactPortal.js).

The portal system is deeply integrated with React’s fiber architecture. When the reconciler encounters a `HostPortal` tag during the render phase, it treats the portal’s children as a separate subtree while maintaining their position in the React component tree. This ensures that context providers and event listeners behave exactly as if the elements were rendered inline.

## React Portal Syntax and API

The public API for creating a react portal is exposed through `ReactDOM.createPortal`:

```javascript
ReactDOM.createPortal(children, container, key?)

```

- **children**: Any valid React node (elements, strings, numbers, fragments, etc.) that should be rendered inside the portal.
- **container**: A DOM element (instance of `Element`, `DocumentFragment`, or `Document`) where the children will be mounted.
- **key** (optional): A unique string or number used by React to identify the portal during reconciliation, useful when rendering multiple portals.

The DOM-specific implementation resides in [`packages/react-dom/src/shared/ReactDOM.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/shared/ReactDOM.js), which forwards the call to the reconciler’s `createPortal` function in [`packages/react-reconciler/src/ReactPortal.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactPortal.js).

## Practical React Portal Examples

### Rendering a Modal Outside the DOM Hierarchy

Modals typically need to overlay the entire page and escape `overflow: hidden` constraints on parent containers. A react portal mounts the modal directly to `document.body`:

```jsx
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

function Modal({ children, onClose }) {
  // Rendered into document.body via a react portal
  return ReactDOM.createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal-content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

export default function App() {
  const [open, setOpen] = useState(false);
  
  return (
    <div>
      <button onClick={() => setOpen(true)}>Open modal</button>
      {open && (
        <Modal onClose={() => setOpen(false)}>
          <h2>Portal Modal</h2>
          <p>This content is rendered outside the #root element.</p>
        </Modal>
      )}
    </div>
  );
}

```

Even though the DOM nodes live inside `document.body`, the `Modal` component participates fully in the React tree: it receives props, can use context, and its events bubble according to the React component hierarchy, not the DOM hierarchy.

### Building a Tooltip with Dynamic Positioning

Tooltips often require precise positioning relative to a trigger element while avoiding clipping by parent containers with `overflow: hidden`:

```jsx
import React, { useRef, useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

function Tooltip({ targetRef, children }) {
  const tooltipRoot = document.getElementById('tooltip-root');
  const [pos, setPos] = useState({ top: 0, left: 0 });

  useEffect(() => {
    if (targetRef.current && tooltipRoot) {
      const rect = targetRef.current.getBoundingClientRect();
      setPos({ top: rect.bottom + 4, left: rect.left });
    }
  }, [targetRef, tooltipRoot]);

  if (!tooltipRoot) return null;

  return ReactDOM.createPortal(
    <div
      className="tooltip"
      style={{ position: 'absolute', top: pos.top, left: pos.left }}
    >
      {children}
    </div>,
    tooltipRoot
  );
}

export default function Example() {
  const buttonRef = useRef(null);
  const [show, setShow] = useState(false);

  return (
    <div>
      <button 
        ref={buttonRef} 
        onMouseEnter={() => setShow(true)} 
        onMouseLeave={() => setShow(false)}
      >
        Hover me
      </button>
      {show && <Tooltip targetRef={buttonRef}>I'm a tooltip</Tooltip>}
    </div>
  );
}

```

This react portal approach ensures the tooltip renders into a dedicated DOM node (e.g., `#tooltip-root`) while maintaining React’s event system and lifecycle management.

### Portaling a List into a Scrollable Sidebar

For widgets or plugin architectures, you may need to inject content into a specific region of the page defined outside your component tree:

```jsx
import ReactDOM from 'react-dom';

function Sidebar({ children }) {
  const sidebarRoot = document.getElementById('sidebar-root');
  
  if (!sidebarRoot) return null;
  
  return ReactDOM.createPortal(
    <nav className="sidebar">{children}</nav>,
    sidebarRoot
  );
}

// Usage
function App() {
  return (
    <div className="main-content">
      <Sidebar>
        <ul>
          <li>Item 1</li>
          <li>Item 2</li>
        </ul>
      </Sidebar>
      <p>Main page content here</p>
    </div>
  );
}

```

## How React Portals Work Under the Hood

The implementation of react portals spans several critical files in the facebook/react repository. Understanding these internals clarifies why portals behave like normal React components despite living in different DOM locations.

### Portal Creation and Typing

In [`packages/react-reconciler/src/ReactPortal.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactPortal.js), the `createPortal` function constructs a `ReactPortal` object:

```javascript
export function createPortal(
  children,
  containerInfo,
  implementation,
  key
) {
  return {
    $$typeof: REACT_PORTAL_TYPE,
    key: key == null ? null : '' + key,
    children,
    containerInfo,
    implementation,
  };
}

```

This object is tagged with `REACT_PORTAL_TYPE` and the `HostPortal` work tag, signaling to the reconciler that this fiber represents a portal rather than a standard DOM element or component.

### Reconciler Integration

The reconciler handles portals in [`packages/react-reconciler/src/ReactFiberBeginWork.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberBeginWork.js) and [`packages/react-reconciler/src/ReactFiberCommitWork.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberCommitWork.js). When encountering a `HostPortal`, the reconciler:

1. **Mounts** the portal’s children into the specified `containerInfo` (the DOM node)
2. **Maintains** the fiber tree structure so that the portal’s children retain their parentage in the React component tree
3. **Propagates** context and events through the React tree, not the DOM tree

This architecture ensures that a react portal behaves transparently to the application code—context providers above the portal still affect components inside it, and events bubble to React event handlers in the component hierarchy regardless of where the DOM nodes are attached.

### Public API Surface

The public-facing API in [`packages/react-dom/src/shared/ReactDOM.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/shared/ReactDOM.js) provides the `createPortal` method that most developers use:

```javascript
function createPortal(children, container, key) {
  return ReactPortal.createPortal(children, container, null, key);
}

```

Additionally, [`packages/react-is/src/index.js`](https://github.com/facebook/react/blob/main/packages/react-is/src/index.js) exports `isPortal` for library authors who need to detect portal objects at runtime.

## When to Use React Portals

React portals solve specific UI architecture problems that standard React rendering cannot address:

- **Modals and Dialogs**: Escape `overflow: hidden` or `z-index` stacking contexts imposed by parent containers. Portaling to `document.body` ensures overlays render above all other content.
- **Tooltips and Popovers**: Position floating elements relative to trigger buttons without being clipped by scrollable ancestors or constrained layouts.
- **Widgets and Plugin Systems**: Inject React components into DOM nodes managed by external scripts or legacy applications, such as sidebars, headers, or third-party containers.
- **Fixed Positioning Edge Cases**: When `position: fixed` behaves unpredictably due to transformed ancestors, portaling to a stable container outside the transformed tree resolves positioning issues.

## Summary

- A **react portal** renders children into a DOM node that exists outside the parent component’s DOM tree using `ReactDOM.createPortal(children, container)`.
- Portals are implemented in [`packages/react-reconciler/src/ReactPortal.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactPortal.js) as `ReactPortal` objects tagged with `HostPortal`, processed by the fiber reconciler to maintain component hierarchy while mounting to external DOM nodes.
- Event bubbling and context propagation work through the React component tree, not the DOM tree, ensuring portals behave like standard components.
- Common use cases include modals, tooltips, and widgets that must break out of CSS containment or `overflow` constraints.

## Frequently Asked Questions

### Do events bubble up through a react portal?

Yes. Despite the DOM nodes being physically separated from the parent component’s DOM element, React maintains the original component tree structure. Events fired inside a portal bubble up to parent React components according to the React component hierarchy, not the DOM hierarchy. This is handled by the reconciler in [`packages/react-reconciler/src/ReactFiberCommitWork.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberCommitWork.js) and the event system.

### Can I use multiple react portals in the same component?

Yes. You can call `ReactDOM.createPortal` multiple times within a single component’s render method, targeting the same container or different containers. Each portal maintains its own fiber subtree and can have independent keys. This is common when rendering multiple modals, tooltips, or widget injections from a single parent component.

### Do react portals work with server-side rendering (SSR)?

React portals require a DOM container to mount into, which does not exist during server-side rendering. When using SSR, you should conditionally create portals only when `document` is available (i.e., in `useEffect` or after hydration). Attempting to call `ReactDOM.createPortal` during the server render will throw an error because the container node cannot be found.

### How do I check if a value is a react portal?

You can use the `ReactIs` utility from the `react-is` package. Specifically, `ReactIs.isPortal(value)` returns `true` if the value is a portal object. This is useful for library authors who need to introspect React element types. The implementation resides in [`packages/react-is/src/index.js`](https://github.com/facebook/react/blob/main/packages/react-is/src/index.js) and checks for the `$$typeof: REACT_PORTAL_TYPE` property that is assigned in [`packages/react-reconciler/src/ReactPortal.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactPortal.js).