Mastering React Portals: Render Components Outside the DOM Hierarchy

Last updated 2 months, 2 weeks ago | 119 views 75     5

Tags:- React

Introduction: Why React Portals Matter

Ever tried building a modal, tooltip, or dropdown menu in React, only to battle with z-indexes, CSS overflows, or scroll clipping?

That's because these UI elements often need to break out of the normal DOM structure to render correctly.

React Portals solve this exact problem.

With React Portals, you can render components outside their parent’s DOM hierarchy, while still preserving React’s event bubbling and reactivity. This is perfect for modals, popovers, tooltips, and toast notifications.


What Are React Portals?

React Portals provide a way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.

ReactDOM.createPortal(child, container)
  • child: The React element you want to render

  • container: A real DOM node (usually outside the #root div)

Introduced in React 16+, this gives you flexibility while keeping React's virtual DOM intact.


When to Use React Portals

Use Portals when:

  • You need to break out of parent styles (e.g. overflow: hidden)

  • You want to render in a completely different part of the DOM

  • You’re building:

    • Modals

    • Tooltips

    • Dropdowns

    • Notifications/Toasts

    • Sidebars


How to Create a React Portal

1. Create a DOM Node in public/index.html

<!-- public/index.html -->
<body>
  <div id="root"></div>
  <div id="portal-root"></div> <!-- This is where we'll inject the portal -->
</body>

2. Use ReactDOM.createPortal()

// Modal.js
import ReactDOM from 'react-dom';

function Modal({ children }) {
  return ReactDOM.createPortal(
    <div className="modal-overlay">
      <div className="modal-content">{children}</div>
    </div>,
    document.getElementById('portal-root') // Inject here
  );
}

export default Modal;

3. Use the Modal in a Component

import Modal from './Modal';
import { useState } from 'react';

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

  return (
    <div>
      <h1>Main App Content</h1>
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      {isOpen && (
        <Modal>
          <h2>This is a Portal Modal</h2>
          <button onClick={() => setIsOpen(false)}>Close</button>
        </Modal>
      )}
    </div>
  );
}

✅ The modal renders outside the main #root, but React still manages its lifecycle and event bubbling.


Complete Working Code Example

<!-- public/index.html -->
<body>
  <div id="root"></div>
  <div id="portal-root"></div>
</body>
// Modal.js
import ReactDOM from 'react-dom';

function Modal({ children }) {
  return ReactDOM.createPortal(
    <div style={modalStyles.overlay}>
      <div style={modalStyles.content}>{children}</div>
    </div>,
    document.getElementById('portal-root')
  );
}

const modalStyles = {
  overlay: {
    position: 'fixed',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    background: 'rgba(0,0,0,0.5)',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
  },
  content: {
    background: '#fff',
    padding: '20px',
    borderRadius: '4px',
    minWidth: '300px',
  },
};

export default Modal;
// App.js
import { useState } from 'react';
import Modal from './Modal';

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

  return (
    <div style={{ padding: '50px' }}>
      <h1>React Portals Example</h1>
      <button onClick={() => setIsOpen(true)}>Show Modal</button>

      {isOpen && (
        <Modal>
          <h2>Portal Modal</h2>
          <p>This modal is rendered outside the root DOM tree!</p>
          <button onClick={() => setIsOpen(false)}>Close</button>
        </Modal>
      )}
    </div>
  );
}

export default App;

Benefits of Using React Portals

Feature React Portal Benefit
DOM flexibility Render in different parts of DOM
Avoids CSS issues Bypass overflow: hidden or z-index clashes
Keeps React logic intact Still uses React's lifecycle, context, and events
Easier to manage overlays No need for separate JS-driven appendChild logic

Tips & Common Pitfalls

Best Practices

  • Add #portal-root near the root DOM structure (but outside #root)

  • Use portals for UI elements that must overlay other elements

  • Make sure your modal supports focus trapping and keyboard accessibility

  • Clean up event listeners or timers when the portal unmounts

Common Mistakes

  • Forgetting to add the portal container (#portal-root) in index.html

  • Using portals unnecessarily for components that don’t need DOM separation

  • Breaking event flow assumptions (e.g., mouse events outside the overlay)


Comparison: React Portals vs Normal DOM Mounting

Feature Standard React Rendering React Portal
Renders inside parent DOM ✅ Yes ❌ No (outside hierarchy)
Avoids layout/CSS conflicts ❌ No ✅ Yes
Keeps React reactivity ✅ Yes ✅ Yes
Suitable for overlays/modals ❌ Harder ✅ Best choice

Conclusion: Build Flexible UIs with React Portals

React Portals are a must-have tool when building overlays, modals, and floating UI elements that break out of the typical DOM structure. With just a few lines of code, you get powerful layout control without sacrificing React’s state and lifecycle features.


Key Takeaways

  • Use ReactDOM.createPortal(child, container) to render outside parent hierarchy

  • Portals are ideal for modals, tooltips, and other overlays

  • Keep your DOM clean and your UI flexible with this powerful pattern