---
title: 'Portals'
description: 'Rendering into out-of-tree DOM nodes'
---

## 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`](https://developer.mozilla.org/en-US/docs/Web/API/Element/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"`.

```rust
use yew::prelude::*;

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

#[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.

## SSR limitation

Portals are **not rendered during server-side rendering**. They require a live
DOM host element (`web_sys::Element`) which is unavailable on the server.
If you need to render content into `<head>` during SSR, see the
[head rendering section](./server-side-rendering#rendering-head-tags)
in the SSR documentation.

## Further reading

- [Portals example](https://github.com/yewstack/yew/tree/master/examples/portals)
