Portal
Renders its children into document.body, or another element, outside the parent DOM tree.
import { useState } from "react";
import { Button, Portal } from "@merid/react";
export function Example() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen((o) => !o)}>{open ? "Remove" : "Render"} banner</Button>
{open ? (
<Portal>
<div role="status" className="floating-banner">
Rendered into document.body
</div>
</Portal>
) : null}
</>
);
}Import
import { Portal } from "@merid/react";Anatomy
Portal is a single component with no markup of its own. Dialog, AlertDialog, Drawer, Popover, Tooltip, DropdownMenu, Select and Toast already portal their content; use Portal directly only for your own floating layers.
Examples
Custom container
Pass container to render into a specific element instead of document.body.
Declared above, rendered in the box:
const [target, setTarget] = useState<HTMLDivElement | null>(null);
return (
<>
<div ref={setTarget} />
<Portal container={target}>Placed inside the target box</Portal>
</>
);Theme, accent, density and direction
Portalled content leaves its DOM subtree, so it would lose a data-theme, data-accent, data-density or dir set on an ancestor. Merid's overlays (Dialog, AlertDialog, Drawer, Popover, DropdownMenu, Select, Tooltip) copy the nearest ancestor values of those attributes from their trigger (for dialogs: the element that had focus when they opened) onto a display: contents .mrd-portal wrapper, so a dark card's menu opens dark and a violet section's select stays violet. Values on <html> or <body> are not copied; the portal inherits those directly. Toasts portal from the provider and follow <html>; the plain Portal component renders its children as they are.
Server rendering
Portal renders nothing on the server, during hydration and while container is null, then mounts on the client. Don't put content in a portal that must be in the initial HTML.
API reference
Portal
| Prop | Type | Default | Description |
|---|---|---|---|
childrenRequired | ReactNode | – | Content rendered into the portal. |
container | Element | null | document.body | Target element. `undefined` uses `document.body`; `null` renders nothing. |
Styling
Portal adds no classes, attributes or CSS variables. Content inside it is still in the React tree (context and events bubble through React), but CSS inherits from the target element, so portalled content is outside any ancestor overflow, transform or stacking context.
Accessibility
Portal is a utility, not a widget, so there is no APG pattern. It changes DOM order, which is also reading and Tab order: when portalled content is interactive, manage focus yourself as the APG dialog pattern describes, or use one of Merid's overlay components, which already do.
Portal has no keyboard interactions of its own.
Guidelines
Do
Avoid