svelte
svelte copied to clipboard
Feature: Let slots be wrapped in if statements to avoid "must be a child of a component" error.
Is your feature request related to a problem? Please describe.
When I do something like the below, I get an error of:
Element with a slot='...' attribute must be a child of a component or a descendant of a custom element
<MyComponent>
{#if something()}
<a slot="right-container">Hi</a>
{/if}
</MyComponent>
Describe the solution you'd like I want to be able to do the first example and wrap my optional slot's in if statements. The reason for this is I actually have default slot text in my component that I want to show.
Describe alternatives you've considered The alternatives is doing something like this which has a lot of duplicate code.
{#if something()}
<MyComponent>
{#if something()}
<a slot="right-container">Hi</a>
{/if}
</MyComponent>
{:else}
<MyComponent />
{/if}
How important is this feature to you? This is a big hassle for me and it would make for a lot cleaner code if I could wrap this slots in if statements. However, it would not affect my ability to code and use svelte.
I would enjoy this feature.
Is there a workaround for now, to have conditional slots? (Except of copy paste..)
+1. this would help make my code less verbose in some cases.
+1, Especially useful for slot forwarding!!! Otherwise there is no way to use the deepest slot fallbacks
https://svelte.dev/repl/7941b94f6c6f42df93aba4d5ef543917?version=3.38.2
I agree, this is a must-have for complex components. Code duplication is one of the largest sources of bugs, please don't make us do it.
Here's the workaround I'm using:
{#if $$slots.header}
<header>
<slot name="header" />
</header>
{/if}
<style>
header {
padding: 16px;
/* ... */
}
header:empty {
display: none;
}
</style>
This means that when you have something like this:
<Demo>
<svelte:fragment slot="header">
{#if condition}
<p>HELLO</p>
{/if}
</svelte:fragment>
</Demo>
You will only see the <header>
– and its padding – when condition
is true.
We just ran into the same thing -- trying to conditionally pass in a slot. Being able to do this makes sense.... In our case, we are trying to conditionally render a slot named "body" within an expandableCard component. If there's no body slot given to expandableCard, just render the header without expander body.
I guess our workaround would be to pass in a Boolean prop to expandableCard called "hasBody", and then conditionally render the expander div that has the <slot name="body">
... clunky but doable.
Hey. It'd be great to get this as a feature.
Even with <svelte:fragment>
the $$slots.name
resolves to true
Issues like this one, the lack of a <svelte:element>
analogous to <svelte:component>
, lack of a simple way to forward all events, impossibility of using actions on components, etc. are some of the rough edges that must be fixed so Svelte can actually be said to be a really mature framework. These things should just work.
Appreciate the CSS tip @lukeed. For anyone using tailwindcss you can do this with the following:
<header class="p-4 empty:p-0">
Well, I currently use this ugly workaround with CSS display instead of if:
<header slot="header" style="display: {data.header.length > 0 ? 'inline' : 'none'}">
(use display depending on your element type, e.g. table-row for table rows etc.)
Also just ran into this, assumed it was a bug rather than a missing feature because it seems like a no brainer. Is there any input from the svelte team on this? Is it due to technical limitations of the compiler? Or do we need an RFC for it? Would love to move it forward.
Is there any progress on getting this over the line? I really don't think that having workarounds is the best way.
+1
This will be really helpful when creating custom component with predefined options and pass fragments to child component.
For example:
I have MyTableComponent
with some classes for SvelteTable
component. To be able to fully use SvelteTable
from my MyTableComponent
, I need to pass 3 slots from MyTableComponent
into MyTableComponent
. Sometimes 0 or only 1 fragment will be overrides.
+1
I think have a workaround. I created a Svelte component that I named SlotFragment.svelte. It conditionally renders a default slot.
{#if $$slots.default}
<slot />
{/if}
For example when composing components, I have component A with a header
slot. The header
slot has some HTML around it that is rendered conditionally on the slot being filled.
I have component B that that renders component A plus some other stuff. Component B provides a header
slot that should be forwarded to component A, plus its own footer
slot.
This syntax in component B works, but causes component A to always think the slot is filled and it renders the additional HTML.
<slot name="header" slot="header" />
So you try to put an #if around it, but the slot must be filled within a component.
{#if $$slots.header}
<slot name="header" slot="header" />
{/if}
Then you try to wrap it in a svelte:fragment, but it can't be in the #if either:
{#if $$slots.header}
<svelte:fragment>
<slot name="header" slot="header" />
<svelte:fragment>
{/if}
However, using the SlotFragment component works:
{#if $$slots.header}
<SlotFragment slot="header">
<slot name="header" />
<SlotFragment>
{/if}
I think have a workaround. I created a Svelte component that I named SlotFragment.svelte. It conditionally renders a default slot.
{#if $$slots.default} <slot /> {/if}
For example when composing components, I have component A with a
header
slot. Theheader
slot has some HTML around it that is rendered conditionally on the slot being filled.I have component B that that renders component A plus some other stuff. Component B provides a
header
slot that should be forwarded to component A, plus its ownfooter
slot.This syntax in component B works, but causes component A to always think the slot is filled and it renders the additional HTML.
<slot name="header" slot="header" />
So you try to put an #if around it, but the slot must be filled within a component.
{#if $$slots.header} <slot name="header" slot="header" /> {/if}
Then you try to wrap it in a svelte:fragment, but it can't be in the #if either:
{#if $$slots.header} <svelte:fragment> <slot name="header" slot="header" /> <svelte:fragment> {/if}
However, using the SlotFragment component works:
{#if $$slots.header} <SlotFragment slot="header"> <slot name="header" /> <SlotFragment> {/if}
I don't think this is compatible with using TypeScript unfortunately.
Also ran into this issue. Conditional slots would be a very useful feature
I ran into this issue yesterday and spun up a repl to reproduce - https://svelte.dev/repl/b089c2c379e9404596445c16311bd1b9?version=3.50.1.
Jup, just ran into this as well. Would love to see this work as the suggested feature. Please add this, Svelte team! 😭💕
Please add this feature, it is deeply important for us to fully use the slot concept. I had to waive the slot feature and fully integrate the child component into the parent one, because of this, which goes against the concept of extracting logic into components.
Now I have to duplicate code in case I would need a similar child component in the future. Which unfortunately is bad practice.
In my case the if and each tag are generating the error.
Thanks
I add my support for the svelte team to add this feature. It's a common problem that currently needs very elaborate workarounds.
+1
+1
It has been more than 2 years that this feature request has been here, with quite a lot of community backing. Is there a timeline for it or is it even something planned to be implemented? I know that there's quite the push to get SvelteKit v1 out, but is there some hope at least of including this in Svelte at any point in the not too distant future @Conduitry @dummdidumm @benmccann ?
I'm just another guy who ran into this issue. Please fix it.
+1
Please don't spam all subscribers to this issue with +1 or the equivelant
Please don't spam all subscribers to this issue with +1.
I don't think this is spamming. It's upvoting, as it's a feature we really really need :)
It is spamming. Please use thumbs up to upvote the issue. I will be hiding your comment as it just adds noise and doesn't help figure out how to implement this
<div hidden={yourCondition} slot="slotName">hide if yourCondition is true else show</div>
This is only working with standard HTML-Elements out of the box. If the child is a custom Component, you could wrap it in a div or modify it to accept a hidden parameter.
The mentioned example would look like this:
<MyComponent> <a hidden={something()} slot="right-container">Hi</a> </MyComponent>
🙃