Currently, Svelte lacks a built-in way to render a component's children at a different location in the DOM tree from where the component itself is mounted. This capability is often referred to as "portalling" or "teleporting" and is useful in a variety of scenarios:
Rendering modals, tooltips, dropdown menus, etc. at the root of the document to ensure they are rendered on top of other content and aren't affected by CSS rules like overflow: hidden on ancestor elements or stacking context hell.
Separating parts of the UI visually from the main component tree for architecture or accessibility purposes.
Decoupling the positioning of elements from the primary document flow.
While it's possible to implement portalling in Svelte currently using a custom action, it has some downsides:
1. Extra DOM Node
Actions would require a DOM node to have the use:portal action, so if you wanted to turn it into a component, capable of wrapping arbitrary children like so:
<Portal>
<span> This is some content I want portalled to the body </span>
</Portal>
The result would look something like this:
<body>
<div id="portal-el-with-action">
<span> This is some content I want portalled to the body </span>
</div>
</body>
The desired result looks like this:
<body>
<span> This is some content I want portalled to the body </span>
</body>
2. Doesn't play well with conditionals
Using the previous <Portal> example, if we had a few levels of components just forwarding props down/setting up context, etc. and there is a conditional at the grandchild level, you have a rogue "portal container" with nothing inside of it inside the portal target, which can have unexpected styling effects, etc.
Competitive Analysis
The other major web frameworks have first-party support for this:
type PortalProps = {
/**
* Where the children will be portalled to. Can either be a selector or an Element.
*
* @defaultValue document.body
*/
target?: string | Element;
/**
* Whether portalling is disabled or not. When disabled, the children will
* be rendered in their normal DOM order.
*
* @defaultValue false
*/
disabled: boolean;
}
### Importance
nice to have
Describe the problem
Currently, Svelte lacks a built-in way to render a component's children at a different location in the DOM tree from where the component itself is mounted. This capability is often referred to as "portalling" or "teleporting" and is useful in a variety of scenarios:
overflow: hidden
on ancestor elements or stacking context hell.While it's possible to implement portalling in Svelte currently using a custom action, it has some downsides:
1. Extra DOM Node
Actions would require a DOM node to have the
use:portal
action, so if you wanted to turn it into a component, capable of wrapping arbitrary children like so:The result would look something like this:
The desired result looks like this:
2. Doesn't play well with conditionals
Using the previous
<Portal>
example, if we had a few levels of components just forwarding props down/setting up context, etc. and there is a conditional at the grandchild level, you have a rogue "portal container" with nothing inside of it inside the portal target, which can have unexpected styling effects, etc.Competitive Analysis
The other major web frameworks have first-party support for this:
createPortal
Teleport
Portal
Describe the proposed solution
Add a
<Portal>
component to Svelte.