React Mastery / Advanced Concepts

Portals

❓What is a React Portal?

React Portals let you render a component outside its parent’s DOM structure, even though it still behaves like a normal child in React.

Normally, a component is rendered inside its parent’s DOM element.

With a Portal, React can place that component somewhere else in the DOM—for example, directly inside <body> or a separate <div> in index.html.

The Syntax

The createPortal function is part of the react-dom package:

ReactDOM.createPortal(child, container)$$
  • child: Anything React can render (JSX, a component, a string, or a fragment).

  • container: A valid DOM element (where the child should be placed).


🧐 Why Use Portals? (The Problem)

In standard rendering, child components are constrained by their parents' CSS. Common issues include:

  • z-index conflicts: A modal inside a deeply nested container might appear behind other elements because of its parent's stacking context.

  • overflow: hidden: If a parent has this property, any child (like a tooltip or dropdown) that extends beyond the parent's boundaries will be cut off.


✅ Implementation Step-by-Step

Step A: Add a Target in index.html

Open your public/index.html and add a new div alongside your main root.

<body>
  <div id="root"></div>
  <div id="modal-root"></div> 
</body>

Step B: Create the Portal Component

import ReactDOM from 'react-dom';

const Modal = ({ children, onClose }) => {
  return ReactDOM.createPortal(
    <div className="modal-overlay">
      <div className="modal-content">
        {children}
        <button onClick={onClose}>Close</button>
      </div>
    </div>,
    document.getElementById('modal-root') // The target node
  );
};

Step C: Using the Portal in App.js

In your main component, you treat the Portal just like any other child component. You can pass props, manage its state, and even wrap it in logic.

import React, { useState } from 'react';
import Modal from './Modal'; // Import the portal component we created

function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div className="app-container" style={{ overflow: 'hidden', position: 'relative' }}>
      <h1>Main Application Content</h1>
      <p>This container has "overflow: hidden", but our modal will still work!</p>
      
      <button onClick={() => setIsOpen(true)}>Open Portal Modal</button>

      {/* The Modal logic remains inside the parent's React tree */}
      {isOpen && (
        <Modal onClose={() => setIsOpen(false)}>
          <h2>Portal Content</h2>
          <p>I am rendered outside the "app-container" DOM node!</p>
        </Modal>
      )}
    </div>
  );
}

export default App;


✏️ Common Use Cases

Use Case

Why Portals?

Modals / Dialogs

To bypass z-index and overflow issues.

Tooltips / Popovers

To ensure they appear on the top layer without being clipped.

Global Notifications

To render toasts at a fixed position regardless of where they are triggered.

Sticky Headers

Sometimes used to move elements to a global "header" area from deep within a page.