Skip to content

Runes mode: cannot send data from child to parent #12905

@rChaoz

Description

@rChaoz

Current state

In Svelte 4 (and Svelte 5 without runes mode), it is very easy to unidirectionally send data from a child to a parent, both static/dynamic data, using props/constants:

<!-- Parent -->
<script>
  let value
</script>

<Child bind:value />

{value}
<!-- Child -->
<script>
  export let value = 123
  // or
  export const value = 123
</script>
<!-- on click: value++ -->

REPL: v4 | v5

The problem

Trying to do the same thing with runes activated in the Child component doesn't work. Trying to bind to an export const fails with the error bind_invalid_export, which then explains it's disallowed to bind to a const. Trying to bind to a $bindable prop instead fails with the error:

props_invalid_value
Cannot do `bind:value={undefined}` when `value` has a fallback value

So, it's become impossible to easily send a value from a child to a parent.

Alternative

Use bind:this , which is harder to use, especially with dynamic values, and more verbose/uglier when you need just one value:

<!-- Parent -->
<script>
  let child
</script>

<Child bind:this={child} />

{child?.staticValue}
{child?.dynamicValue?.value}
<!-- Child -->
<script>
  export const staticValue = 100
  export const dynamicValue = $state({ value: 123 })
</script>
<!-- on click: dynamicValue.value++ -->

Use case

My current use case is something like:

<script>
  let instances = []
</script>

{#each someArray as value, i}
  <Child {value} bind:instance={instances[i]} />
{/each}

The child component has an instance of a third-party library class that attaches to a DOM element, like so:

<script>
  export const instance = new LibraryClass(...)
  let elem
  onMount(() => {
    instance.init(elem)
    return () => instance.destroy()
  })
</script>

<div bind:this={elem}></div>

Proposed solution

Either re-allow the previous behaviour of binding to constants or to props with a default without passing a value, or introduce a directive that is specifically for passing data from a child to a parent, like get:childValue={value}. It should be documented that any changes to value will not be passed to the child and will be overwritten whenever the child emits a new value, and maybe the compiler can even emit errors/warnings when value is mutated directly, but I think it can get difficult to do so (e.g. get:childValue={some.deep[0].state}). The get: solution I think makes more sense since bind is supposed to be bi-directional.

Severity

annoyance

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