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"orrole="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