Skip to content

[feature] Add Portals to Svelte #7082

@ITenthusiasm

Description

@ITenthusiasm

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions