Skip to main content
Version: Next

Portals

What is a portal?

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component. yew::create_portal(child, host) returns a Html value that renders child not hierarchically under its parent component, but as a child of the host element.

Usage

Typical uses of portals can include modal dialogs and hovercards, as well as more technical applications such as controlling the contents of an element's shadowRoot, appending stylesheets to the surrounding document's <head> and collecting referenced elements inside a central <defs> element of an <svg>.

Note that yew::create_portal is a low-level building block. Libraries should use it to implement higher-level APIs which can then be consumed by applications. For example, here is a simple modal dialogue that renders its children into an element outside yew's control, identified by the id="modal_host".

use yew::prelude::*;

#[derive(Properties, PartialEq)]
pub struct ModalProps {
#[prop_or_default]
pub children: Children,
}

#[function_component]
fn Modal(props: &ModalProps) -> Html {
let modal_host = gloo::utils::document()
.get_element_by_id("modal_host")
.expect("Expected to find a #modal_host element");

create_portal(
html!{ {for props.children.iter()} },
modal_host.into(),
)
}

Event handling

Events emitted on elements inside portals follow the virtual DOM when bubbling up. That is, if a portal is rendered as the child of an element, then an event listener on that element will catch events dispatched from inside the portal, even if the portal renders its contents in an unrelated location in the actual DOM.

This allows developers to be oblivious of whether a component they consume, is implemented with or without portals. Events fired on its children will bubble up regardless.

A known issue is that events from portals into closed shadow roots will be dispatched twice, once targeting the element inside the shadow root and once targeting the host element itself. Keep in mind that open shadow roots work fine. If this impacts you, feel free to open a bug report about it.

Further reading