sveltesveltekitsvelte-componentsvelte-preprocesssvelte-4

Conditional default slot detection in svelte


I have a svelte component with a default slot and a named slot. I have an instance of that component and use the named slot, and use the default slow inside an {#if} statement checking for a boolean.

The problem is inside my component, I have logic that checks for the default slot and renders some padding and border. It seems even when the boolean is false and the {#if} is false, that the default slow is still there, just empty.

Child:

const hasDefaultSlot = Object.keys($$slots).includes('default');

Parent:

<MyComponent>
<div slot="header"> header </div>
{#if checked}
  <div>body</div>
{/if}
</MyComponent>

My expectation is that when checked is false, Object.keys($$slots).includes('default') would be false, or $$slots.default would be false, or that $$slots.default wouldn't be a boolean, but an object that contains other info that I can use to check if it's empty.

Sadly, it seems the Svelte compiler sees the #if statement result as always true, even though it changes in real time and starts off false.

Without being able to conditionally detect the slot, I am always rendering the body and body styles for a card item, which messes up the css for the component.


Solution

  • Everything inside the component that is not explicitly assigned to a named slot becomes part of the default slot. The {#if} is also part of the slot, so the slot will always be set.

    You can work around this by adding an additional flag property as you mentioned.


    In Svelte 5 this will be a bit different with the more flexible snippets system. Like with the default slot there is an implicit assignment to a children property but this property can be assigned like any other, so the contents can be passed conditionally.

    {#snippet body()}
      <div>body</div>
    {/snippet}
    <MyComponent children={checked ? body : undefined}>
      {#snippet header()}
        <div>header</div>
      {/snippet}
    </MyComponent>