Skip to content

Double declaration of $$slots when using $props() with named slots #15836

Open
@thomasvaeth

Description

@thomasvaeth

Describe the bug

Bug Report: Double declaration of $$slots when using $props() with named slots

When using Svelte 5's runes and $props() in a component that has named slots and spreads remaining props, the compiler generates code that declares $$slots twice, causing a build error.

Expected Behavior

The component should compile successfully since $$slots is a special Svelte variable that should be handled by the runtime.

Actual Behavior

The compiled output tries to declare $$slots twice:

  1. First from sanitize_slots($$props)
  2. Then again when destructuring from $props()

Additional Context

  • This only occurs when using named slots (<slot name="x">)
  • This happens when using $props() with runes enabled
  • The issue appears to be in how the compiler handles the interaction between runes' $props() and Svelte's built-in slot handling

Workarounds

There are two ways to work around this issue:

Don't use prop spreading in components with named slots:

<script>
  const { class: className = '' } = $props();
  // Don't use ...rest
</script>

<div class="slot-test {className}">
  <slot name="header" />
</div>

Don't use $props() in components that need both named slots and prop spreading:

<script>
  export let className = '';
  // Use traditional props instead of $props()
</script>

<div {...$$restProps} class="slot-test {className}">
  <slot name="header" />
</div>

Reproduction

  1. Create a component that uses runes, $props(), and named slots:
<!-- SlotTest.svelte -->
<svelte:options runes={true} />

<script lang="ts">
  const { class: className = '', ...rest } = $props();
</script>

<div {...rest} class="slot-test {className}">
  {#if $$slots.header}
    <div class="header">
      <slot name="header" />
    </div>
  {/if}
  
  {#if $$slots.footer}
    <div class="footer">
      <slot name="footer" />
    </div>
  {/if}
</div>
  1. Use the component with named slots:
<!-- App.svelte -->
<script>
  import SlotTest from './SlotTest.svelte';
</script>

<SlotTest data-testid="test">
  <svelte:fragment slot="header">
    Header Content
  </svelte:fragment>
  <svelte:fragment slot="footer">
    Footer Content
  </svelte:fragment>
</SlotTest>

Logs

error during build:
Identifier "$$slots" has already been declared

 7: export default function SlotTest($$payload, $$props) {
 8:   const $$slots = $.sanitize_slots($$props);
 9:   const { $$slots, $$events, ...props } = $$props;
              ^

System Info

System:
  OS: macOS 15.4.1
  CPU: (12) arm64 Apple M2 Max
  Memory: 3.31 GB / 64.00 GB
  Shell: 5.9 - /bin/zsh
Binaries:
  Node: 20.17.0 - ~/.nvm/versions/node/v20.17.0/bin/node
  Yarn: 1.22.22 - ~/.yarn/bin/yarn
  npm: 10.8.2 - ~/.nvm/versions/node/v20.17.0/bin/npm
  pnpm: 10.8.1 - ~/.nvm/versions/node/v20.17.0/bin/pnpm
  Watchman: 2025.04.07.00 - /opt/homebrew/bin/watchman
Browsers:
  Brave Browser: 130.1.71.118
  Chrome: 135.0.7049.115
  Safari: 18.4
npmPackages:
  svelte: ^5.20.2 => 5.25.10

Severity

blocking an upgrade

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