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 an 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: Html,
}
#[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(
props.children.clone(),
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.