Portal
Mount children into another element while keeping them in the logical tree.
Portal renders its children into a different DOM parent (document.body by default), escaping ancestor stacking contexts and overflow clipping. Dialogs, dropdowns, and toasts are the usual customers.
import { Portal } from "@implementjs/core";
If(open).Then(
Portal(Div({ class: "fixed inset-0 grid place-items-center bg-black/50" }, DialogPanel())),
);
Choosing a target
Chain .To(target) or use the props form. The target may be an element or a Readable of one (children move when it changes):
Portal(Toast(message)).To(toastRoot);
Portal({ to: overlayRoot }, DialogPanel());
Disabling
disabled mounts the children in place (in the normal parent) instead of teleporting them. It's bindable, so a signal can toggle it at runtime. This is useful for nested overlays that should stay inside their parent overlay:
Portal({ to: document.body, disabled: isNested }, Menu());
Portal(Menu()).Disabled(isNested);
Still part of the tree
Teleporting only moves the DOM. The children keep their position in the logical tree, which means:
- Context lookups resolve from where the
Portalis declared. - Unmounting the portal (or the
Ifbranch around it) unmounts the teleported children. - Errors inside the portal reach the error boundary wrapping the portal.
That's composition covered. The next part steps outside your component tree entirely and works with the document itself, starting with raw HTML.