svelte
svelte copied to clipboard
Slotted content for Web Component nested inside of a Svelte component is not applied to the Web Component
Given a Web Component which expects slotted children, which itself is nested inside of a Svelte component, as in the following:
<SvelteComponent>
<my-custom-element>
<div slot="slot1">my-custom-element content</div>
<div slot="slot2">more my-custom-element content</div>
</my-custom-element>
</SvelteComponent>
...the slotted children are not applied to the Web Component.
I have reproduced the issue here (just run npm install and npm run build and then open dist/index.html in your browser):
https://github.com/therealnicksaunders/svelte/tree/issue/slotted-content
In a more complicated example, this actually produces an error which suggests that a slotted child is being applied to the Svelte component instead of the Web Component in which it is nested. We have this Svelte-generated code:
(0, _shared.append)(pagecontent._slotted.header, div);
The referenced div is nested inside of a Web Component; its "slot" attribute of "header" corresponds to a slot inside of that Web Component rather than the PageContent Svelte component (which has no named slots).
Hopefully I have provided enough info here... Please let me know if I can clarify anything.
Thanks in advance for your help looking into it!
I am also experiencing this issue, Svelte is highjacking slots from HTML custom elements.
Also affected. In my company, we are actually thinking of migrating big parts of our infrastructure in svelte. But without a solution on that issue, we will not be able to use our (already developed) custom elements. This would be a pity because svelte is a really awesome framework. Please consider giving a proper solution, or at least a workaround on that.
Thanks in advance!
Edit: I found a workaround using the "use:" directive and add the slot attribute manually. This way, I can use slots within if blocks and everything. Svelte does not try to interpret it:
<my-web-component>
{#if showSlots}
<div use:addNamedSlot={"subtitle"}>My subtitle</div>
<div use:addNamedSlot={"footer"}>My footer</div>
{/if}
</my-web-component>
//... and my fucntion addNamedSlot:
export function addNamedSlot(node: HTMLElement, name: string) { //if you don't use ts, just remove the :Type from the params
node.setAttribute("slot", name);
}
I actually implemented the addNamedSlot in an other file so that I can import it and re-use it.
Still, it would be very nice to have a way saying to svelte: hey, do not try to interpret my slot tags and slot attributes, just render them as you would with any other attribute/tagname.
A simple workaround is to put your custom component into an html element. Then the html element will target the appropriate slot. e.g.
<div slot="footer"> <!-- html element which targets the appropriate slot -->
<Button caption="Save" /> <!-- Custom svelte component -->
</div>
@nsaunders your repro is not found https://github.com/therealnicksaunders/svelte/tree/issue/slotted-content, would it be possible to help recreate the repro so i can take a look at it?
Closing due to inactivity, will re-open if a repro appears.
I tried to make a CodeSandBox to demonstrate this issue. I am using custom elements from Ionic.
Using an html element like span does work. However for certain other Custom Element components having this extra span tag breaks the display of the Custom Element.
Using svelte:fragment would be great but I have not found a way to get it to forward to the correct Custom Element slot.
The Ionic Toolbar component has these slots defined...

I have the Svelte Toolbar component configured as

Here is the result of my testing in the CodeSandBox. Hopefully I didn't make too many mistakes π

CodeSandBox π¨βπ»
Hey @tanhauhau I added an example in the above comment.
Do you think we can take a look and see if the issue can be reopened? βΊοΈ
copied the code sandbox into https://svelte.dev/repl/cfe22283aa194419887fd45f9775a245?version=3.38.3
Hi
using the UI5 REPL available through the mentioned link I managed to implement slot content projection (is that the proper word?) into the svelte component for Ionic components (as @RobertWeaver & @tanhauhau tried). With a few amendments. I did not use $$restProps nor svelte:fragment
This as part of the project to have all Ionic elements have Svelte wrappers (90 components).
So my understanding (and this is working in practice for me) that there are two steps here needed to realise this -
Step 1. adding the slot definitions within the <slot name="slotname"> in the component. This tells the Svelte compiler these slots are OK - but does not yet project to content into it.
Step 2. using $$props to spread the slot definition into the webcomponent.
The Svelte wrapper IonItem.svelte for webcomponent ion-item would then look like this:
<script lang="ts">
import type { Color, AnimationBuilder, RouterDirection } from "@ionic/core";
import { IonItem } from "@ionic/core/components/ion-item";
import { defineComponent } from "ionic-svelte"; // code that registers the webcomponent
//@ts-ignore
export let color: Color = undefined;
//@ts-ignore
export let button = false;
...all sorts of props
defineComponent("ion-item", IonItem);
</script>
<ion-item
{color}
....other props
{lines}
{counter}
{routerAnimation}
{routerDirection}
{target}
{type}
{counterFormatter}
{...$$props} // this propagates the slot names and the content
on:focus
on:blur
..... other events
on:click
><slot /><slot name="start" /><slot name="end" /><slot name="error" /><slot name="helper" />
</ion-item>
Now I don't have any issues getting the content in the right slot and no typescript issues. Here the full code for this wrapper - https://github.com/Tommertom/svelte-ionic-npm/blob/main/experimental/components/IonItem.svelte
But now there are other issues now arising, getting webcomponents to work with Svelte wrappers, as internal stuff in the webcomponent library cannot target elements properly anymore...... not sure if this is because the setup I have choosen.
ps.
The alternative I had going on earlier was using a shim property called ionSlot that would use {#if ionSlot=... blocks to hardcode slot content in the webcomponent. And then I also had to use an Empty svelte component with all slot definitions to avoid complaints from the Svelte compiler. I felt this was not the best way to go.
Potentially relates to #8457 and #3128.