@ydant/portal
v0.3.0
Published
Portal spell for rendering into alternate DOM targets
Downloads
22
Readme
@ydant/portal
Portal spell for rendering content into alternate DOM targets.
Installation
pnpm add @ydant/portalUsage
import { scope } from "@ydant/core";
import { createDOMBackend, createBasePlugin, div, text } from "@ydant/base";
import { createPortalPlugin, portal } from "@ydant/portal";
const modalContainer = document.getElementById("modals")!;
function* App() {
yield* div("Main content");
yield* portal(modalContainer, () => [div({ class: "modal" }, "I render outside the main tree!")]);
}
scope(createDOMBackend(document.getElementById("app")!), [
createBasePlugin(),
createPortalPlugin(),
]).mount(App);The portal's children render into modalContainer instead of the app root. This is useful for modals, tooltips, and dropdowns that need to escape their parent's overflow or stacking context.
API
portal(target, content)
function* portal(target: unknown, content: Builder): Spell<"portal">;Renders content into target instead of the current parent node.
target— The DOM node (or any render target node) to render into.content— A builder function that yields the portal's children.
When the parent scope is unmounted or refreshed, the portal's children are cleaned up (via clearChildren on the target).
createPortalPlugin()
function createPortalPlugin(): Plugin;Creates the portal plugin. Must be registered alongside createBasePlugin().
The plugin declares a dependency on "base" and handles "portal" type requests.
Limitations
- Multiple portals to the same target — When two portals share the same target node and one is unmounted (e.g., via
Slot.refresh()),clearChildrenremoves all children from the target, including the other portal's content. Use separate target nodes for independent portals. - Lifecycle hooks require element scope —
onMountandonUnmountwork inside portal content, but they must be placed inside an element (e.g., insidediv()), not at the portal's root level.
