Portal

Render content outside the DOM tree to escape CSS stacking contexts and layering restrictions.

Portal

The Portal component renders its children into a different DOM node, typically document.body. Use it to escape CSS stacking contexts, z-index layering, and overflow clipping—useful for modals, dropdowns, tooltips, and floating content.

Portal is the foundation for floating UI patterns. It teleports your content to a different part of the DOM without adding extra wrapper elements.


Features

  • Renders children into a target DOM node (defaults to document.body)
  • Escapes CSS stacking contexts and overflow clipping
  • Automatically handles SSR (no-op on server)
  • Forwards ref to the rendered container

Usage

import { Portal } from '@loke/ui/portal';

export default function Example() {
  return (
    <div>
      <p>Content in normal flow</p>
      <Portal>
        <div className="fixed inset-0 bg-black/50">
          Modal overlay
        </div>
      </Portal>
    </div>
  );
}

Props

Prop

Type


Examples

Inspect the DOM — the toast appears directly under <body>, outside this component tree

Default (renders to body)

<Portal>
  <div className="fixed top-4 right-4 bg-white p-4 rounded shadow">
    Toast message
  </div>
</Portal>

Custom container

const [container, setContainer] = React.useState<HTMLDivElement | null>(null);

return (
  <>
    <div ref={setContainer} id="modal-root" />
    <Portal container={container}>
      <div className="fixed inset-0 flex items-center justify-center">
        Custom modal
      </div>
    </Portal>
  </>
);

Accessibility

Portal itself has no accessibility requirements, but content rendered via Portal should follow normal accessibility patterns:

  • Modals should use role="dialog" or role="alertdialog"
  • Focus should be managed (trapped within the portal for modals)
  • Escape key should close modals when appropriate
  • Overlays should not interfere with screen reader announcements

Use Portal alongside FocusScope and DismissableLayer for complete modal/dialog implementations.


Best practices

  • Combine with FocusScope to trap focus inside modals
  • Use with DismissableLayer to detect outside clicks and Escape key
  • For modals, render to document.body to avoid stacking context issues
  • Prefer Portal for non-dismissable floating content (tooltips, popovers); consider Dialog for user-facing modals