DEV Community

Abhay Singh Kathayat
Abhay Singh Kathayat

Posted on

Understanding React Portals: How to Render Outside the DOM Hierarchy

Portals in React

In React, a Portal provides a way to render children into a DOM node that exists outside the hierarchy of the parent component. While React typically renders components inside the root DOM element, Portals allow you to render content outside the parent component’s DOM structure, which is useful for situations like modals, tooltips, and overlays.

Portals are particularly useful when you need to manage components that should visually appear on top of other components (like modals, dropdowns, or popups) without disrupting the regular DOM hierarchy of your React app.


How Portals Work

React Portals provide a way to escape the normal parent-child DOM relationship and render a component’s children into a different part of the DOM tree.

In a typical React component hierarchy, when you render a component, it is inserted as a child of the parent component. With portals, React allows you to render the component outside of this normal hierarchy.

Basic Syntax

import ReactDOM from 'react-dom';

const MyPortal = () => {
  return ReactDOM.createPortal(
    <div>
      <h2>This is rendered using a Portal</h2>
    </div>,
    document.getElementById('portal-root') // Target DOM node where the portal content will be rendered
  );
};
Enter fullscreen mode Exit fullscreen mode

Key Points About Portals

  • Parent-child relationship: The component rendered through a portal doesn’t break its relationship with the parent component. It’s still considered a child, but it’s rendered in a different part of the DOM.

  • No impact on React component hierarchy: The parent-child hierarchy in React remains unchanged. The rendered portal content will still behave as if it is part of the parent component (e.g., receiving props, state management, etc.).

  • Use cases: Portals are ideal for rendering components like modals, tooltips, dropdowns, or anything that should visually appear outside the regular component structure. They are particularly useful in cases where positioning content outside the parent element is necessary.


Creating a Portal

React provides the ReactDOM.createPortal() function to create a portal. It takes two arguments:

  1. Children: The React elements you want to render.
  2. Target DOM node: The DOM node in which the portal’s children should be rendered. This can be any valid DOM node.

Example:

import ReactDOM from 'react-dom';

const MyModal = ({ isOpen, onClose }) => {
  if (!isOpen) return null; // If the modal is not open, return nothing.

  return ReactDOM.createPortal(
    <div className="modal-overlay">
      <div className="modal-content">
        <button onClick={onClose}>Close</button>
        <h2>This is a modal rendered using a Portal!</h2>
      </div>
    </div>,
    document.getElementById('modal-root') // The DOM node outside the parent component
  );
};
Enter fullscreen mode Exit fullscreen mode

In this example, the modal content is rendered outside the current component hierarchy but still maintains React's lifecycle and state management.


Portals and DOM Manipulation

Portals can be helpful when you need to control the DOM structure for specific components but don’t want to manipulate the global DOM manually. This is especially useful when:

  • The modal, tooltip, or overlay needs to render outside of the usual parent-child component relationship.
  • You want to control stacking order (e.g., layering) or need specific DOM manipulation (e.g., for animations or z-index layering).

Example: Modal Using Portals

Consider an example where you need to create a modal that should be rendered on top of other content without affecting the layout of the main content. Using a portal allows the modal to be appended to a separate part of the DOM, such as a dedicated modal container.

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

const App = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);

  const openModal = () => setIsModalOpen(true);
  const closeModal = () => setIsModalOpen(false);

  return (
    <div>
      <h1>Welcome to the React App</h1>
      <button onClick={openModal}>Open Modal</button>

      <Modal isOpen={isModalOpen} onClose={closeModal} />
    </div>
  );
};

const Modal = ({ isOpen, onClose }) => {
  if (!isOpen) return null;

  return ReactDOM.createPortal(
    <div className="modal-overlay">
      <div className="modal-content">
        <button onClick={onClose}>Close</button>
        <h2>This is a Modal!</h2>
      </div>
    </div>,
    document.getElementById('modal-root') // A div in the HTML body with id 'modal-root'
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

In the index.html file, you would need a <div> with the ID modal-root:

<body>
  <div id="root"></div>
  <div id="modal-root"></div> <!-- The portal target -->
</body>
Enter fullscreen mode Exit fullscreen mode

Why Use Portals?

  1. Improved UI control: With portals, you can position elements like modals and tooltips anywhere in the DOM while still keeping React in control of the component tree.

  2. Preventing CSS issues: Portals help avoid layout issues, especially with z-index stacking and overflow properties, because they render outside of the typical parent component.

  3. Accessibility: Portals are beneficial for managing accessibility elements like modals or alerts, as they can be rendered in a way that makes them part of the document flow but without affecting the rest of the UI structure.

  4. Non-disruptive rendering: Portals let you render elements like floating UI components in a way that doesn't interfere with the DOM structure.


Portal with Event Listeners

Since the portal content is rendered outside the parent, you may need to manually handle event listeners for elements like modals or tooltips.

For example, if you want to close a modal when clicking outside of it, you can add an event listener for clicks outside the modal’s bounds. This can be handled within the portal content itself.

import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';

const Modal = ({ isOpen, onClose }) => {
  if (!isOpen) return null;

  useEffect(() => {
    const handleClickOutside = (e) => {
      if (e.target.classList.contains('modal-overlay')) {
        onClose();
      }
    };

    // Add event listener when modal opens
    document.addEventListener('click', handleClickOutside);

    // Clean up event listener when modal closes
    return () => {
      document.removeEventListener('click', handleClickOutside);
    };
  }, [onClose]);

  return ReactDOM.createPortal(
    <div className="modal-overlay">
      <div className="modal-content">
        <button onClick={onClose}>Close</button>
        <h2>This is a Modal!</h2>
      </div>
    </div>,
    document.getElementById('modal-root')
  );
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

Portals in React provide a powerful tool for rendering content outside the normal DOM hierarchy while maintaining all the benefits of React's component structure and lifecycle. They are particularly useful for elements like modals, tooltips, or overlays, where the visual display needs to be outside of the usual DOM flow, yet the component should still be controlled by React. This flexibility ensures that you can manage complex UI components without compromising performance or layout integrity.


Top comments (0)