I have a SvelteKit app using Svelte 5. My app shows a basket, using a BasketLine
component to show every product in the basket with its quantity:
<script lang="ts">
interface Props {
line: BasketLineWithOffers;
}
const { line }: Props = $props();
</script>
<div>
<h2>{line.product.title}</h2>
<NumberPicker min={1} bind:value={line.quantity} />
</div>
This is giving a runtime warning in the console:
[svelte] binding_property_non_reactive
bind:value={line.quantity}
(src/lib/basket/BasketLine.svelte:96:30) is binding to a non-reactive property https://svelte.dev/e/binding_property_non_reactive
Using the number picker (a simple component showing a number input as well as plus and minus button) doesn't change the quantity.
Using const { line = $bindable() }: Props = $props();
doesn't fix it either. So what is the correct way to pass a bindable line
property to the BasketLine
component in such a way that its properties are also bindable/reactive state? line
is just a simple JSON object coming from the server.
In runes mode, the object passed in needs to be reactive, i.e. $state
, otherwise modifications of the properties will not trigger updates. You will get warnings if you modify a property that is not marked $bindable
but this has no runtime effect when modifying the property rather than reassigning it.
To get rid of the warnings, the property should be declared $bindable
and used with bind:line
on the parent.
E.g.
<script>
import Component from './Component.svelte';
let line = $state({ quantity: 1 });
</script>
<Component bind:line />
<!-- Component.svelte -->
<script>
const { line = $bindable() } = $props();
</script>
<input type=number min={1} bind:value={line.quantity} /> <br>
Quantity: {line.quantity}
If you cannot control how the parent passes the object and you need local reactivity, you probably need to declare a local $state
variable from the property (and possibly synchronize it on prop change via an $effect
).