Skip to content

Portal

wybthon.portal

portal

Portal component for rendering children into a different DOM container.

Use Portal to render content outside of the current component's DOM ancestor while keeping it part of the same reactive ownership tree (so signals, effects, and context still work). Common use cases include modals, tooltips, and toast notifications.

Functions:

Name Description
Portal

Render children into a different DOM container.

Portal

Portal(children: Any = None, *, mount: Any = 'body') -> VNode

Render children into a different DOM container.

Matches SolidJS's <Portal mount={...}>. The children mount into mount (by default document.body) while remaining linked to the surrounding component's reactive scope, so signals, context, and lifecycle hooks still apply, and they're removed when the portal unmounts.

Parameters:

Name Type Description Default
children Any

A VNode, a list of VNodes, or a zero-arg callable (rendered as a reactive hole).

None
mount Any

The target container: an Element instance, a CSS selector string, or a kernel node id. Defaults to "body".

'body'

Returns:

Type Description
VNode

A component VNode whose children render inside mount.

Note

A portal renders nothing during a server render. Its children mount in the browser once the page has hydrated.

Example
Show(
    show_modal,
    lambda: Portal(
        div(p("Modal content"), class_="modal"),
        mount="#modal-root",
    ),
)

What's in this module

Portal renders its children into a different DOM container while keeping them inside the current reactive ownership tree, so signals, context, cleanups, and error boundaries behave exactly as they would for in-place children. It's the tool for modals, tooltips, popovers, and toasts that must escape their parent's layout.

Name Description
Portal Portal(children, *, mount="body"); children is a VNode, a list, or a zero-arg callable (rendered as a hole); mount is an Element, a CSS selector, or a kernel node id.
from wybthon import Portal, Show, button, component, create_signal, div, p


@component
def Modal():
    is_open, set_open = create_signal(False)

    def toggle():
        set_open(lambda v: not v)

    return div(
        button("Open", on_click=toggle),
        Show(
            is_open,
            lambda: Portal(
                div(p("I'm in #modal-root"), button("Close", on_click=toggle), class_="modal"),
                mount="#modal-root",
            ),
        ),
    )
  • Content is removed when the portal's owner is disposed (here, when Show flips back to falsy).
  • The mount target becomes an event delegation root for as long as the portal is mounted, so on_* handlers inside it fire even when the target sits outside the container passed to render (a <body>-level modal layer, for example).
  • Context flows through the reactive tree, not the DOM tree, so use_context inside the portal sees the surrounding providers.

See also