Skip to content

Different reactivity triggering of props (automatic derived inserted sometimes) #15406

@thes01

Description

@thes01

Describe the bug

I wonder why sometimes a prop getter is wrapped in $derived, such as:
<Component name={state.name.toString()}/>

which results in the following compiled output:

const expression = $.derived(() => $.get(state).name.toString());

Component(node, {
	get name() {
		return $.get(expression);
	}
});

and other times it is used directly in the getter:
<Component name={state.name}/>

which results in:

Component(node, {
	get name() {
		return $.get(state).name;
	}
});

This behavior seems to be present in Svelte since v5.0. The specific issue I'm having is that this has consequences for reactivity triggering inside the child components. The minimal reproduction is in the REPL.

I assume there is some reason to do this. However, this feels inconsistent (magical) to me. In our codebase, we sometimes want to rely on the "not derived" behavior, i.e. update things even when the content is the same.

An example use case where this actually matters would be having an input field that triggers some on_change when a user inputs something. But in some logic above the resulting value is computed/overwritten to be the same as had been before (thus it should overwrite back what user has typed in). By wrapping the prop in $derived, the signal doesn't update the prop value, so the user's input stays in the input field.

Reproduction

https://svelte.dev/playground/0e8e303022c24b609f4c13b58ef2df9d?version=5.20.5

Remove .toString() on line 7 in App.svelte and now the compiled output doesn't use $derived.

Logs

System Info

reproducible in REPL

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