Portals and DOM Rendering Control

1. createPortal for DOM Teleportation

Feature Syntax Description Use Case
createPortal createPortal(child, container) Render children into different DOM node Modals, tooltips, dropdowns outside parent DOM
Target Container document.getElementById('id') DOM element to render portal into Dedicated portal root, body, custom container
Event Bubbling Bubbles through React tree Events bubble to React parent, not DOM parent Event handlers work as expected in React
Context Inherits from React tree Portal components access parent context Theme, auth, state available in portals
Multiple Portals Multiple createPortal calls Render to different containers Multiple modals, tooltips, overlays

Example: Basic portal setup

import { createPortal } from 'react-dom';

// HTML structure needed
// <div id="root"></div>
// <div id="portal-root"></div>

function App() {
  return (
    <div className="app">
      <h1>My App</h1>
      <PortalExample />
    </div>
  );
}

function PortalExample() {
  const [show, setShow] = useState(false);
  
  return (
    <div>
      <button onClick={() => setShow(true)}>
        Show Portal
      </button>
      
      {show && (
        createPortal(
          <div className="portal-content">
            <h2>I'm rendered in #portal-root!</h2>
            <button onClick={() => setShow(false)}>
              Close
            </button>
          </div>,
          document.getElementById('portal-root')
        )
      )}
    </div>
  );
}

// Portal component wrapper
function Portal({ children, container = 'portal-root' }) {
  const [mounted, setMounted] = useState(false);
  
  useEffect(() => {
    setMounted(true);
    return () => setMounted(false);
  }, []);
  
  if (!mounted) return null;
  
  const portalRoot = document.getElementById(container);
  if (!portalRoot) {
    console.error(`Portal container #${container} not found`);
    return null;
  }
  
  return createPortal(children, portalRoot);
}

Example: Dynamic portal container

// Create portal container on demand
function usePortal(id = 'portal-root') {
  const [container, setContainer] = useState(null);
  
  useEffect(() => {
    // Check if container exists
    let element = document.getElementById(id);
    
    // Create if doesn't exist
    if (!element) {
      element = document.createElement('div');
      element.id = id;
      document.body.appendChild(element);
    }
    
    setContainer(element);
    
    // Cleanup - remove if we created it
    return () => {
      if (element && element.childNodes.length === 0) {
        element.remove();
      }
    };
  }, [id]);
  
  return container;
}

// Usage
function MyComponent() {
  const portalContainer = usePortal('my-portal');
  
  if (!portalContainer) return null;
  
  return createPortal(
    <div>Portal content</div>,
    portalContainer
  );
}
Portal Benefits: Render outside parent DOM hierarchy, avoid CSS overflow/z-index issues, maintain React component tree structure, events bubble through React tree, access parent context and props.

2. Modal Components with Portals

Feature Implementation Description Benefit
Overlay/Backdrop Full-screen div with fixed position Semi-transparent background behind modal Focus attention, prevent background interaction
Body Scroll Lock document.body.style.overflow Disable scrolling when modal open Keep user focused on modal content
Focus Trap Manage focus within modal Tab cycles through modal elements only Keyboard accessibility, prevents focus escape
ESC to Close Keyboard event listener Close modal on Escape key Expected behavior, better UX
Click Outside Backdrop click handler Close modal when clicking backdrop Intuitive dismissal, flexible UX
import { createPortal } from 'react-dom';
import { useEffect, useRef } from 'react';

function Modal({ isOpen, onClose, children, title }) {
  const modalRef = useRef(null);
  
  // Lock body scroll when modal open
  useEffect(() => {
    if (isOpen) {
      document.body.style.overflow = 'hidden';
      return () => {
        document.body.style.overflow = 'unset';
      };
    }
  }, [isOpen]);
  
  // Close on ESC key
  useEffect(() => {
    if (!isOpen) return;
    
    const handleEscape = (e) => {
      if (e.key === 'Escape') onClose();
    };
    
    document.addEventListener('keydown', handleEscape);
    return () => document.removeEventListener('keydown', handleEscape);
  }, [isOpen, onClose]);
  
  // Focus trap
  useEffect(() => {
    if (!isOpen || !modalRef.current) return;
    
    const focusableElements = modalRef.current.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );
    
    const firstElement = focusableElements[0];
    const lastElement = focusableElements[focusableElements.length - 1];
    
    // Focus first element
    firstElement?.focus();
    
    const handleTab = (e) => {
      if (e.key !== 'Tab') return;
      
      if (e.shiftKey) {
        if (document.activeElement === firstElement) {
          e.preventDefault();
          lastElement?.focus();
        }
      } else {
        if (document.activeElement === lastElement) {
          e.preventDefault();
          firstElement?.focus();
        }
      }
    };
    
    document.addEventListener('keydown', handleTab);
    return () => document.removeEventListener('keydown', handleTab);
  }, [isOpen]);
  
  if (!isOpen) return null;
  
  return createPortal(
    <div className="modal-overlay" onClick={onClose}>
      <div 
        ref={modalRef}
        className="modal-content"
        onClick={(e) => e.stopPropagation()} // Prevent close on content click
        role="dialog"
        aria-modal="true"
        aria-labelledby="modal-title"
      >
        <div className="modal-header">
          <h2 id="modal-title">{title}</h2>
          <button 
            onClick={onClose}
            aria-label="Close modal"
            className="modal-close"
          >
            ×
          </button>
        </div>
        <div className="modal-body">
          {children}
        </div>
      </div>
    </div>,
    document.body
  );
}

// Usage
function App() {
  const [isModalOpen, setIsModalOpen] = useState(false);
  
  return (
    <div>
      <button onClick={() => setIsModalOpen(true)}>
        Open Modal
      </button>
      
      <Modal 
        isOpen={isModalOpen}
        onClose={() => setIsModalOpen(false)}
        title="My Modal"
      >
        <p>Modal content goes here</p>
        <button onClick={() => setIsModalOpen(false)}>
          Close
        </button>
      </Modal>
    </div>
  );
}

Example: Modal with animation

import { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';

function AnimatedModal({ isOpen, onClose, children }) {
  const [shouldRender, setShouldRender] = useState(false);
  const [isAnimating, setIsAnimating] = useState(false);
  
  useEffect(() => {
    if (isOpen) {
      setShouldRender(true);
      // Trigger animation after render
      setTimeout(() => setIsAnimating(true), 10);
    } else {
      setIsAnimating(false);
      // Remove from DOM after animation
      setTimeout(() => setShouldRender(false), 300);
    }
  }, [isOpen]);
  
  if (!shouldRender) return null;
  
  return createPortal(
    <div 
      className={`modal-overlay ${isAnimating ? 'open' : ''}`}
      onClick={onClose}
    >
      <div 
        className={`modal-content ${isAnimating ? 'open' : ''}`}
        onClick={(e) => e.stopPropagation()}
      >
        {children}
      </div>
    </div>,
    document.body
  );
}

/* CSS for animation
.modal-overlay {
  opacity: 0;
  transition: opacity 300ms ease-in-out;
}

.modal-overlay.open {
  opacity: 1;
}

.modal-content {
  transform: scale(0.7);
  opacity: 0;
  transition: all 300ms ease-in-out;
}

.modal-content.open {
  transform: scale(1);
  opacity: 1;
}
*/
Warning: Always lock body scroll, implement keyboard accessibility (ESC, focus trap), use proper ARIA attributes, clean up event listeners, test with screen readers.

3. Tooltip and Overlay Positioning

Technique Implementation Description Use Case
getBoundingClientRect Element position calculation Get trigger element position and size Position tooltip relative to trigger
Positioning Library Popper.js, Floating UI Smart positioning with collision detection Complex tooltips, dropdowns, popovers
Dynamic Position Calculate on show/scroll/resize Update position when trigger moves Scrollable containers, responsive layouts
Flip/Shift Adjust if outside viewport Prevent tooltip from being cut off Edges of screen, small viewports
Arrow Positioning Calculated arrow pointer position Arrow points to trigger element Visual connection between elements

Example: Basic tooltip with positioning

import { useState, useRef, useEffect } from 'react';
import { createPortal } from 'react-dom';

function Tooltip({ children, content, position = 'top' }) {
  const [isVisible, setIsVisible] = useState(false);
  const [coords, setCoords] = useState({ top: 0, left: 0 });
  const triggerRef = useRef(null);
  const tooltipRef = useRef(null);
  
  const calculatePosition = () => {
    if (!triggerRef.current || !tooltipRef.current) return;
    
    const triggerRect = triggerRef.current.getBoundingClientRect();
    const tooltipRect = tooltipRef.current.getBoundingClientRect();
    
    let top = 0;
    let left = 0;
    const gap = 8; // Space between trigger and tooltip
    
    switch (position) {
      case 'top':
        top = triggerRect.top - tooltipRect.height - gap;
        left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
        break;
      case 'bottom':
        top = triggerRect.bottom + gap;
        left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
        break;
      case 'left':
        top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
        left = triggerRect.left - tooltipRect.width - gap;
        break;
      case 'right':
        top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
        left = triggerRect.right + gap;
        break;
    }
    
    // Prevent overflow
    top = Math.max(gap, Math.min(top, window.innerHeight - tooltipRect.height - gap));
    left = Math.max(gap, Math.min(left, window.innerWidth - tooltipRect.width - gap));
    
    setCoords({ top, left });
  };
  
  useEffect(() => {
    if (isVisible) {
      calculatePosition();
      window.addEventListener('scroll', calculatePosition);
      window.addEventListener('resize', calculatePosition);
      
      return () => {
        window.removeEventListener('scroll', calculatePosition);
        window.removeEventListener('resize', calculatePosition);
      };
    }
  }, [isVisible]);
  
  return (
    <>
      <span
        ref={triggerRef}
        onMouseEnter={() => setIsVisible(true)}
        onMouseLeave={() => setIsVisible(false)}
        onFocus={() => setIsVisible(true)}
        onBlur={() => setIsVisible(false)}
      >
        {children}
      </span>
      
      {isVisible && createPortal(
        <div
          ref={tooltipRef}
          className="tooltip"
          role="tooltip"
          style={{
            position: 'fixed',
            top: `${coords.top}px`,
            left: `${coords.left}px`,
            zIndex: 9999
          }}
        >
          {content}
        </div>,
        document.body
      )}
    </>
  );
}

// Usage
<p>
  Hover over <Tooltip content="This is a tooltip!">
    <strong>this text</strong>
  </Tooltip> to see tooltip.
</p>

Example: Advanced positioning with Floating UI

import { useFloating, flip, shift, offset, arrow } from '@floating-ui/react';
import { useRef } from 'react';
import { createPortal } from 'react-dom';

function AdvancedTooltip({ children, content }) {
  const [isOpen, setIsOpen] = useState(false);
  const arrowRef = useRef(null);
  
  const { x, y, strategy, refs, middlewareData } = useFloating({
    open: isOpen,
    placement: 'top',
    middleware: [
      offset(8), // Gap between trigger and tooltip
      flip(), // Flip to opposite side if no space
      shift({ padding: 8 }), // Shift to stay in viewport
      arrow({ element: arrowRef }) // Position arrow
    ]
  });
  
  const arrowX = middlewareData.arrow?.x;
  const arrowY = middlewareData.arrow?.y;
  
  return (
    <>
      <span
        ref={refs.setReference}
        onMouseEnter={() => setIsOpen(true)}
        onMouseLeave={() => setIsOpen(false)}
      >
        {children}
      </span>
      
      {isOpen && createPortal(
        <div
          ref={refs.setFloating}
          style={{
            position: strategy,
            top: y ?? 0,
            left: x ?? 0,
            zIndex: 9999
          }}
          className="tooltip"
        >
          {content}
          <div
            ref={arrowRef}
            style={{
              position: 'absolute',
              left: arrowX != null ? `${arrowX}px` : '',
              top: arrowY != null ? `${arrowY}px` : '',
              transform: 'rotate(45deg)'
            }}
            className="tooltip-arrow"
          />
        </div>,
        document.body
      )}
    </>
  );
}
Positioning Best Practices: Use positioning libraries for complex cases, recalculate on scroll/resize, prevent viewport overflow, add collision detection, position arrow correctly, test on mobile devices.

4. Event Bubbling and Portal Event Handling

Concept Behavior Description Implication
React Tree Bubbling Events bubble through React tree Not DOM tree - React component hierarchy Parent handlers work even though DOM separate
onClick Propagation Bubbles to React parent Portal click triggers parent onClick Event handlers work naturally
stopPropagation e.stopPropagation() Stop event from bubbling in React tree Prevent parent handlers from firing
Context Access Portal accesses parent context Context flows through React tree Portal components have full context access
Native Events Don't bubble through portals addEventListener on DOM doesn't cross portal Use React events, not native listeners

Example: Event bubbling demonstration

import { createPortal } from 'react-dom';

function EventBubblingDemo() {
  const [log, setLog] = useState([]);
  
  const addLog = (message) => {
    setLog(prev => [...prev, message]);
  };
  
  return (
    <div 
      onClick={() => addLog('Parent clicked')}
      style={{ padding: '20px', border: '2px solid blue' }}
    >
      <h3>Parent Component</h3>
      
      <button onClick={() => addLog('Regular button clicked')}>
        Regular Button (in DOM tree)
      </button>
      
      {createPortal(
        <div 
          style={{ 
            position: 'fixed', 
            top: '100px', 
            right: '20px',
            padding: '20px',
            border: '2px solid red',
            background: 'white'
          }}
        >
          <h3>Portal Component</h3>
          <p>I'm rendered outside the parent DOM!</p>
          
          {/* This click WILL bubble to parent in React tree */}
          <button onClick={() => addLog('Portal button clicked')}>
            Portal Button
          </button>
          
          {/* This prevents bubbling */}
          <button onClick={(e) => {
            e.stopPropagation();
            addLog('Portal button (no bubble) clicked');
          }}>
            Portal Button (stopPropagation)
          </button>
        </div>,
        document.body
      )}
      
      <div style={{ marginTop: '20px' }}>
        <h4>Event Log:</h4>
        <ul>
          {log.map((item, i) => (
            <li key={i}>{item}</li>
          ))}
        </ul>
      </div>
    </div>
  );
}

// Result: Portal button click triggers BOTH:
// 1. "Portal button clicked"
// 2. "Parent clicked" (bubbled through React tree)

// But "Portal button (no bubble)" only triggers:
// 1. "Portal button (no bubble) clicked"
// (stopPropagation prevents parent handler)

Example: Context access in portals

import { createContext, useContext } from 'react';
import { createPortal } from 'react-dom';

const ThemeContext = createContext('light');

function App() {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={theme}>
      <div className={`app ${theme}`}>
        <h1>Main App</h1>
        <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
          Toggle Theme
        </button>
        
        <PortalComponent />
      </div>
    </ThemeContext.Provider>
  );
}

function PortalComponent() {
  // Portal component can access parent context!
  const theme = useContext(ThemeContext);
  
  return createPortal(
    <div className={`portal ${theme}`}>
      <h2>I'm in a portal</h2>
      <p>Current theme: {theme}</p>
      <p>Context works across portals!</p>
    </div>,
    document.body
  );
}

// Theme updates in parent flow to portal component
// Even though rendered in different DOM location
Event Bubbling Key Points: React events bubble through component tree not DOM tree, portal events reach parent handlers naturally, use stopPropagation to prevent bubbling, context and props work across portals, native DOM events don't cross portal boundary.

5. Accessibility Considerations with Portals

Concern Solution ARIA Attribute Why Important
Screen Reader Announcement Use proper role attributes role="dialog" Announce modal/dialog to screen readers
Modal State Indicate modal is active aria-modal="true" Screen reader understands context
Label Association Link title to dialog aria-labelledby Identify dialog purpose
Focus Management Move focus to modal Focus first element Keyboard users start in modal
Focus Trap Keep focus within modal Tab cycling Prevent accessing hidden content
Return Focus Restore focus after close Store trigger element User returns to where they were
Hidden Content Mark background as hidden aria-hidden="true" Screen readers skip background

Example: Accessible modal with full ARIA support

import { useEffect, useRef } from 'react';
import { createPortal } from 'react-dom';

function AccessibleModal({ isOpen, onClose, title, children }) {
  const modalRef = useRef(null);
  const triggerRef = useRef(document.activeElement);
  const mainContentRef = useRef(document.getElementById('main-content'));
  
  // Focus management
  useEffect(() => {
    if (!isOpen) return;
    
    // Store trigger element
    triggerRef.current = document.activeElement;
    
    // Hide main content from screen readers
    if (mainContentRef.current) {
      mainContentRef.current.setAttribute('aria-hidden', 'true');
      mainContentRef.current.setAttribute('inert', ''); // Prevent interaction
    }
    
    // Focus first focusable element in modal
    const focusable = modalRef.current?.querySelector(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );
    focusable?.focus();
    
    return () => {
      // Restore main content
      if (mainContentRef.current) {
        mainContentRef.current.removeAttribute('aria-hidden');
        mainContentRef.current.removeAttribute('inert');
      }
      
      // Return focus to trigger
      if (triggerRef.current instanceof HTMLElement) {
        triggerRef.current.focus();
      }
    };
  }, [isOpen]);
  
  // ESC and focus trap
  useEffect(() => {
    if (!isOpen || !modalRef.current) return;
    
    const handleKeyDown = (e) => {
      // Close on ESC
      if (e.key === 'Escape') {
        onClose();
        return;
      }
      
      // Focus trap
      if (e.key === 'Tab') {
        const focusableElements = modalRef.current.querySelectorAll(
          'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
        );
        
        const firstElement = focusableElements[0];
        const lastElement = focusableElements[focusableElements.length - 1];
        
        if (e.shiftKey) {
          if (document.activeElement === firstElement) {
            e.preventDefault();
            lastElement?.focus();
          }
        } else {
          if (document.activeElement === lastElement) {
            e.preventDefault();
            firstElement?.focus();
          }
        }
      }
    };
    
    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
  }, [isOpen, onClose]);
  
  if (!isOpen) return null;
  
  return createPortal(
    <div 
      className="modal-overlay"
      onClick={onClose}
      role="presentation"
    >
      <div
        ref={modalRef}
        className="modal-content"
        onClick={(e) => e.stopPropagation()}
        role="dialog"
        aria-modal="true"
        aria-labelledby="modal-title"
        aria-describedby="modal-description"
      >
        <div className="modal-header">
          <h2 id="modal-title">{title}</h2>
          <button
            onClick={onClose}
            aria-label="Close dialog"
            className="modal-close"
          >
            <span aria-hidden="true">×</span>
          </button>
        </div>
        
        <div id="modal-description" className="modal-body">
          {children}
        </div>
      </div>
    </div>,
    document.body
  );
}

Example: Accessible tooltip

function AccessibleTooltip({ children, content, id }) {
  const [isVisible, setIsVisible] = useState(false);
  const tooltipId = id || `tooltip-${Math.random().toString(36).substr(2, 9)}`;
  
  return (
    <>
      <span
        onMouseEnter={() => setIsVisible(true)}
        onMouseLeave={() => setIsVisible(false)}
        onFocus={() => setIsVisible(true)}
        onBlur={() => setIsVisible(false)}
        aria-describedby={isVisible ? tooltipId : undefined}
        tabIndex={0} // Make keyboard accessible
      >
        {children}
      </span>
      
      {isVisible && createPortal(
        <div
          id={tooltipId}
          role="tooltip"
          className="tooltip"
          style={{
            position: 'fixed',
            // ... positioning logic
          }}
        >
          {content}
        </div>,
        document.body
      )}
    </>
  );
}

// ARIA attributes explained:
// - aria-describedby: Links tooltip to trigger
// - role="tooltip": Identifies as tooltip
// - tabIndex={0}: Makes keyboard accessible
Accessibility Requirements: Always manage focus properly, implement keyboard navigation (ESC, Tab), use proper ARIA attributes, hide background content, return focus on close, test with screen readers (NVDA, JAWS, VoiceOver), support keyboard-only navigation.

6. Multiple Portal Management Patterns

Pattern Implementation Description Use Case
Portal Stack Array of active portals Track z-index order of portals Multiple modals, layered overlays
Portal Manager Context-based management Centralized portal state and control Complex apps with many portals
Modal Queue Queue system for modals Show modals sequentially Onboarding flows, tutorials
Z-Index Management Auto-increment z-index Ensure correct stacking order Prevent overlap issues
Cleanup on Unmount Remove portal containers Clean up DOM when done Prevent memory leaks, clean DOM

Example: Portal manager with context

import { createContext, useContext, useState } from 'react';
import { createPortal } from 'react-dom';

// Portal manager context
const PortalContext = createContext(null);

export function PortalProvider({ children }) {
  const [portals, setPortals] = useState([]);
  
  const addPortal = (portal) => {
    const id = Math.random().toString(36).substr(2, 9);
    setPortals(prev => [...prev, { ...portal, id, zIndex: 1000 + prev.length }]);
    return id;
  };
  
  const removePortal = (id) => {
    setPortals(prev => prev.filter(p => p.id !== id));
  };
  
  const closeTopPortal = () => {
    setPortals(prev => prev.slice(0, -1));
  };
  
  return (
    <PortalContext.Provider value={{ portals, addPortal, removePortal, closeTopPortal }}>
      {children}
      {portals.map(portal =>
        createPortal(
          <div key={portal.id} style={{ zIndex: portal.zIndex }}>
            {portal.content}
          </div>,
          document.body
        )
      )}
    </PortalContext.Provider>
  );
}

// Hook to use portal manager
export function usePortal() {
  const context = useContext(PortalContext);
  if (!context) {
    throw new Error('usePortal must be used within PortalProvider');
  }
  return context;
}

// Modal component using portal manager
function ManagedModal({ isOpen, onClose, children, title }) {
  const { addPortal, removePortal } = usePortal();
  const portalIdRef = useRef(null);
  
  useEffect(() => {
    if (isOpen) {
      portalIdRef.current = addPortal({
        content: (
          <div className="modal-overlay" onClick={onClose}>
            <div className="modal-content" onClick={(e) => e.stopPropagation()}>
              <h2>{title}</h2>
              {children}
            </div>
          </div>
        )
      });
    } else if (portalIdRef.current) {
      removePortal(portalIdRef.current);
      portalIdRef.current = null;
    }
    
    return () => {
      if (portalIdRef.current) {
        removePortal(portalIdRef.current);
      }
    };
  }, [isOpen, title, children]);
  
  return null;
}

// Usage
function App() {
  return (
    <PortalProvider>
      <MyApp />
    </PortalProvider>
  );
}

Example: Modal queue system

function useModalQueue() {
  const [queue, setQueue] = useState([]);
  const [currentModal, setCurrentModal] = useState(null);
  
  const addToQueue = (modal) => {
    setQueue(prev => [...prev, modal]);
  };
  
  const showNext = () => {
    if (queue.length > 0) {
      setCurrentModal(queue[0]);
      setQueue(prev => prev.slice(1));
    } else {
      setCurrentModal(null);
    }
  };
  
  const closeModal = () => {
    setCurrentModal(null);
    // Show next in queue after delay
    setTimeout(showNext, 300);
  };
  
  // Auto-show first modal
  useEffect(() => {
    if (!currentModal && queue.length > 0) {
      showNext();
    }
  }, [queue, currentModal]);
  
  return { currentModal, addToQueue, closeModal, queueLength: queue.length };
}

// Usage - onboarding flow
function OnboardingFlow() {
  const { currentModal, addToQueue, closeModal } = useModalQueue();
  
  useEffect(() => {
    // Queue multiple modals
    addToQueue({ title: 'Welcome', content: 'Step 1...' });
    addToQueue({ title: 'Features', content: 'Step 2...' });
    addToQueue({ title: 'Get Started', content: 'Step 3...' });
  }, []);
  
  return (
    <>
      {currentModal && (
        <Modal
          isOpen={true}
          onClose={closeModal}
          title={currentModal.title}
        >
          {currentModal.content}
        </Modal>
      )}
    </>
  );
}
Multiple Portal Best Practices: Use portal manager for complex apps, implement z-index management, handle ESC key to close top portal, clean up portals on unmount, consider modal queue for flows, test stacking behavior thoroughly.

Portals and DOM Rendering Best Practices