Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Add Portals to Svelte #7082

Open
ITenthusiasm opened this issue Dec 30, 2021 · 9 comments
Open

[feature] Add Portals to Svelte #7082

ITenthusiasm opened this issue Dec 30, 2021 · 9 comments

Comments

@ITenthusiasm
Copy link

Describe the problem

I'm aware that this discussion has been split across a few issues. However, none of those issues have been explicit feature requests for portals. Some issues have been closed early, or been distracted from the original topic, or been too narrow (e.g., <:Body> tags). Thus, I'm creating this guy to make a central, focused issue around portals (with new thoughts).

The problem that this feature would address is the need for clean, well-supported portals in svelte. I believe @arggh caught most of the common use cases in their comment on another issue, such as modals and popovers. These use cases are pretty common and therefore can't really be ignored.

I'm aware of other solutions that have spun off from other discussions, such as @ThomasJuster's solution, and the solution that gained inspiration from it: svelte-portal. While these solutions are clever, they are still flawed for multiple reasons:

  1. They pollute the DOM unnecessarily. Regarding the first solution, it permanently pollutes the DOM unnecessarily. (The wrapping <div class="portal-clone"> empties its children, but the node still remains). Regarding svelte-portal, it temporarily pollutes the DOM unnecessarily. (More accurately, it temporarily renders to an "incorrect" location in the DOM, then moves the node to the correct location.)
  2. As many have pointed out, the existing solutions are a bit hackish. They basically render hidden content, move the content, and then unhide the content.
  3. Because the existing approaches are workarounds/hacks and not native features, they have their own sets of bugs. Last month a bug was reported at use portal inside keyed_each block romkor/svelte-portal#101. It seems that issue may not be addressable.
  4. Because the existing approaches basically do "DOM dancing", they can run the risk of temporarily creating invalid HTML. For instance, I'm working on a form where people can add items which get rendered to a table. Each row of the table is clickable, opening up a modal for editing (this isn't the place to debate table edit approaches). I can circumvent the need for creating state variables by using another form in the modal that "submits" (saves) the data. But nesting forms is forbidden. I know the nested form would appear only temporarily with the existing approaches, but that still shouldn't be necessary.

Arguments can be made for workarounds, workarounds, and more workarounds to defer responsibility to devs. ("Why are you using modal edits for tables?" "Is using a form to avoid state variables really that advantageous?" "Maybe you can try some tricks with position: fixed for modals?" etc.) But ultimately these workarounds ignore the real question, and they fail because:

  1. They put svelte users in an unnecessarily inconvenient situation. (For example, I shouldn't have to create external, self-closing form elements and tediously pollute my DOM elements with form attributes.)
  2. They can't get away from valid edge cases.

Other well-established frameworks like React and Vue have supported portal features because this is a common, basic use case. Svelte would benefit from this as well.

Describe the proposed solution

A clear new feature like <svelte:portal to={LOCATION}>CONTENT</svelte:portal> would be sufficient. This solution should render CONTENT directly to the correct LOCATION, instead of doing an awkward "DOM dance". If it's an unnecessary amount of effort to support selectors like Vue, it should be sufficient to require explicit DOM Nodes like React. After all, it's easy enough to say document.getElementById("ID"), and this would give the user more granular control over what node is used. (I'd probably prefer selectors.)

This solution would automatically resolve the request for <:Body /> tags (#1133). And it has the added bonus of providing flexibility for where the portal sends content.


I'm aware that @Conduitry has brought up SSR concerns across a few of these floating issues. I agree with @arggh that ignoring portals would be fine (and even desirable) in SSR.

Other well-respected frameworks like Next.js seem to avoid it altogether. For instance, timneutkens himself pointed to the official example for portals, which still only runs client-side. (If the component name wasn't obvious, useEffect never runs server-side.) Nuxt.js 3 is still in beta, so I don't know what they'll do about teleport. But I'm assuming it's possible they'll ignore teleport too. As has been said previously, most users are expecting to use portals client-side anyway.

Alternatives considered

The alternatives I considered were already mentioned in the opening problem statement. However, none of these alternatives seem sufficient on their own.

Importance

would make my life easier

@ITenthusiasm
Copy link
Author

If ignoring the portal server-side isn't desirable, some potential SSR approaches were mentioned. I'm not aware of Svelte's internals, but perhaps another solution is the following:

While the HTML to render is being generated server-side, <svelte:portal />s could be excluded from the originally rendered HTML. Instead, they'd be added to a separate array/map of sorts that tracks the portals that need to be rendered later. This list would use the LOCATION (denoted by to) for keys and the physical CONTENT for values. Once the HTML is generated, the process would check for any potential portals that need to be resolved. If none are present, the HTML gets sent to the client immediately. If portals are present, the process could loop through each portal and use pattern matching to place portals in the proper location. Then the HTML would get sent to the client.

For instance <svelte:portal to="#modals"> would denote that the HTML string should be searched for the first occurrence of id="modals". The portal's content string could be inserted either right after the opening tag or right before the closing tag.

If we're going with the approach of ignoring portals server-side, then we'd just exclude portals from the rendered HTML.

@ITenthusiasm
Copy link
Author

Related (and potentially related) issues: #3088, #1133, #4237 (becomes obsolete if this feature is addressed), #4036

Outside sveltejs/svelte: romkor/svelte-portal#101

@ITenthusiasm ITenthusiasm changed the title Add Portals to Svelte [feature] Add Portals to Svelte Dec 30, 2021
@YeungKC
Copy link

YeungKC commented Dec 12, 2022

I solved this issue, this is what I created https://github.com/YeungKC/svelte-portal

No dom move
Support SSR
Support update

But I still think svelte should provide <portal >

@Algoinde
Copy link

Algoinde commented Jan 20, 2023

No dom move Support SSR Support update

No ability to pass <slot> as well (as it uses svelte:component).

I really hope this feature gets implemented.

@Char-Ten
Copy link

Here is my hack:

<script>
function portal(node,{to}){
    const target = document.querySelector(to);
    target&&target.appendChild(node);
    return {}
}
</script>
<div use:portal={{to:'body'}}></div>

It's works.
But I don't know if it has any side effects.

@divdavem
Copy link

divdavem commented Apr 4, 2023

No ability to pass <slot> as well (as it uses svelte:component).

If #8067 was implemented, it would be easy to support slots as well.

@ITenthusiasm
Copy link
Author

It's a bit unfortunate to say this given the additional interest shown by the other votes on the OP... But this feature request (and its brothers, such as #4036) may not be valid anymore. As I mentioned previously, I agree with someone else's statement that the large majority of the use cases for portals are probably Modals. However, now we have the standardized dialog element. So that defeats the need for portals in many situations.

There's perhaps a smaller set of scenarios where portals are needed for other things like popovers. But now we have browsers rushing to support the Popover API. If this API gets adopted, Svelte may not need to bother with portals at all considering that: A) Popovers will be a part of the native spec and B) We already have an [albeit-undesirable] workaround in Svelte.

Pinging @Conduitry because you commented on #4036 and @pngwn because you've managed the tags on that issue. I just want to make sure that this is a visible consideration. Not sure where Svelte stands on portals at the moment. I'm only leaving this open in case Svelte is interested in supporting portals; otherwise, I might close.


If people are aware of other use cases that browsers aren't ultimately intending to support, that might change things... maybe. But for now, it seems like browsers are seeking to give us what we need.

@KoRnFactory
Copy link

A Portal component is now really easy to achieve thanks to slots just being props in Svelte 5.

Playgound Demo

@Fevol
Copy link

Fevol commented Aug 3, 2024

A Portal component is now really easy to achieve thanks to slots just being props in Svelte 5.

Playgound Demo

Small note for people who stumble on this answer when searching for Portals in Svelte 5, createRoot was removed during development, so the code should be changed to use mount instead. Other than that, nothing changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants