13 KiB
Data ordinarily flows down, from parent to child. The bind:
directive allows data to flow the other way, from child to parent.
The general syntax is bind:property={expression}
, where expression
is an lvalue (i.e. a variable or an object property). When the expression is an identifier with the same name as the property, we can omit the expression — in other words these are equivalent:
<input bind:value={value} />
<input bind:value />
Svelte creates an event listener that updates the bound value. If an element already has a listener for the same event, that listener will be fired before the bound value is updated.
Most bindings are two-way, meaning that changes to the value will affect the element and vice versa. A few bindings are readonly, meaning that changing their value will have no effect on the element.
Function bindings
You can also use bind:property={get, set}
, where get
and set
are functions, allowing you to perform validation and transformation:
<input bind:value={
() => value,
(v) => value = v.toLowerCase()}
/>
In the case of readonly bindings like dimension bindings, the get
value should be null
:
<div
bind:clientWidth={null, redraw}
bind:clientHeight={null, redraw}
>...</div>
[!NOTE] Function bindings are available in Svelte 5.9.0 and newer.
<input bind:value>
A bind:value
directive on an <input>
element binds the input's value
property:
<script>
let message = $state('hello');
</script>
<input bind:value={message} />
<p>{message}</p>
In the case of a numeric input (type="number"
or type="range"
), the value will be coerced to a number (demo):
<script>
let a = $state(1);
let b = $state(2);
</script>
<label>
<input type="number" bind:value={a} min="0" max="10" />
<input type="range" bind:value={a} min="0" max="10" />
</label>
<label>
<input type="number" bind:value={b} min="0" max="10" />
<input type="range" bind:value={b} min="0" max="10" />
</label>
<p>{a} + {b} = {a + b}</p>
If the input is empty or invalid (in the case of type="number"
), the value is undefined
.
Since 5.6.0, if an <input>
has a defaultValue
and is part of a form, it will revert to that value instead of the empty string when the form is reset. Note that for the initial render the value of the binding takes precedence unless it is null
or undefined
.
<script>
let value = $state('');
</script>
<form>
<input bind:value defaultValue="not the empty string">
<input type="reset" value="Reset">
</form>
[!NOTE] Use reset buttons sparingly, and ensure that users won't accidentally click them while trying to submit the form.
<input bind:checked>
Checkbox and radio inputs can be bound with bind:checked
:
<label>
<input type="checkbox" bind:checked={accepted} />
Accept terms and conditions
</label>
Since 5.6.0, if an <input>
has a defaultChecked
attribute and is part of a form, it will revert to that value instead of false
when the form is reset. Note that for the initial render the value of the binding takes precedence unless it is null
or undefined
.
<script>
let checked = $state(true);
</script>
<form>
<input type="checkbox" bind:checked defaultChecked={true}>
<input type="reset" value="Reset">
</form>
<input bind:group>
Inputs that work together can use bind:group
.
<script>
let tortilla = $state('Plain');
/** @type {Array<string>} */
let fillings = $state([]);
</script>
<!-- grouped radio inputs are mutually exclusive -->
<input type="radio" bind:group={tortilla} value="Plain" />
<input type="radio" bind:group={tortilla} value="Whole wheat" />
<input type="radio" bind:group={tortilla} value="Spinach" />
<!-- grouped checkbox inputs populate an array -->
<input type="checkbox" bind:group={fillings} value="Rice" />
<input type="checkbox" bind:group={fillings} value="Beans" />
<input type="checkbox" bind:group={fillings} value="Cheese" />
<input type="checkbox" bind:group={fillings} value="Guac (extra)" />
[!NOTE]
bind:group
only works if the inputs are in the same Svelte component.
<input bind:files>
On <input>
elements with type="file"
, you can use bind:files
to get the FileList
of selected files. When you want to update the files programmatically, you always need to use a FileList
object. Currently FileList
objects cannot be constructed directly, so you need to create a new DataTransfer
object and get files
from there.
<script>
let files = $state();
function clear() {
files = new DataTransfer().files; // null or undefined does not work
}
</script>
<label for="avatar">Upload a picture:</label>
<input accept="image/png, image/jpeg" bind:files id="avatar" name="avatar" type="file" />
<button onclick={clear}>clear</button>
FileList
objects also cannot be modified, so if you want to e.g. delete a single file from the list, you need to create a new DataTransfer
object and add the files you want to keep.
[!NOTE]
DataTransfer
may not be available in server-side JS runtimes. Leaving the state that is bound tofiles
uninitialized prevents potential errors if components are server-side rendered.
<select bind:value>
A <select>
value binding corresponds to the value
property on the selected <option>
, which can be any value (not just strings, as is normally the case in the DOM).
<select bind:value={selected}>
<option value={a}>a</option>
<option value={b}>b</option>
<option value={c}>c</option>
</select>
A <select multiple>
element behaves similarly to a checkbox group. The bound variable is an array with an entry corresponding to the value
property of each selected <option>
.
<select multiple bind:value={fillings}>
<option value="Rice">Rice</option>
<option value="Beans">Beans</option>
<option value="Cheese">Cheese</option>
<option value="Guac (extra)">Guac (extra)</option>
</select>
When the value of an <option>
matches its text content, the attribute can be omitted.
<select multiple bind:value={fillings}>
<option>Rice</option>
<option>Beans</option>
<option>Cheese</option>
<option>Guac (extra)</option>
</select>
You can give the <select>
a default value by adding a selected
attribute to the<option>
(or options, in the case of <select multiple>
) that should be initially selected. If the <select>
is part of a form, it will revert to that selection when the form is reset. Note that for the initial render the value of the binding takes precedence if it's not undefined
.
<select bind:value={selected}>
<option value={a}>a</option>
<option value={b} selected>b</option>
<option value={c}>c</option>
</select>
<audio>
<audio>
elements have their own set of bindings — five two-way ones...
...and seven readonly ones:
<audio src={clip} bind:duration bind:currentTime bind:paused></audio>
<video>
<video>
elements have all the same bindings as [#audio] elements, plus readonly videoWidth
and videoHeight
bindings.
<img>
<img>
elements have two readonly bindings:
<details bind:open>
<details>
elements support binding to the open
property.
<details bind:open={isOpen}>
<summary>How do you comfort a JavaScript bug?</summary>
<p>You console it.</p>
</details>
Contenteditable bindings
Elements with the contenteditable
attribute support the following bindings:
[!NOTE] There are subtle differences between
innerText
andtextContent
.
<div contenteditable="true" bind:innerHTML={html} />
Dimensions
All visible elements have the following readonly bindings, measured with a ResizeObserver
:
<div bind:offsetWidth={width} bind:offsetHeight={height}>
<Chart {width} {height} />
</div>
[!NOTE]
display: inline
elements do not have a width or height (except for elements with 'intrinsic' dimensions, like<img>
and<canvas>
), and cannot be observed with aResizeObserver
. You will need to change thedisplay
style of these elements to something else, such asinline-block
.
bind:this
<!--- copy: false --->
bind:this={dom_node}
To get a reference to a DOM node, use bind:this
. The value will be undefined
until the component is mounted — in other words, you should read it inside an effect or an event handler, but not during component initialisation:
<script>
/** @type {HTMLCanvasElement} */
let canvas;
$effect(() => {
const ctx = canvas.getContext('2d');
drawStuff(ctx);
});
</script>
<canvas bind:this={canvas} />
Components also support bind:this
, allowing you to interact with component instances programmatically.
<!--- file: App.svelte --->
<ShoppingCart bind:this={cart} />
<button onclick={() => cart.empty()}> Empty shopping cart </button>
<!--- file: ShoppingCart.svelte --->
<script>
// All instance exports are available on the instance object
export function empty() {
// ...
}
</script>
bind:property for components
bind:property={variable}
You can bind to component props using the same syntax as for elements.
<Keypad bind:value={pin} />
While Svelte props are reactive without binding, that reactivity only flows downward into the component by default. Using bind:property
allows changes to the property from within the component to flow back up out of the component.
To mark a property as bindable, use the $bindable
rune:
<script>
let { readonlyProperty, bindableProperty = $bindable() } = $props();
</script>
Declaring a property as bindable means it can be used using bind:
, not that it must be used using bind:
.
Bindable properties can have a fallback value:
<script>
let { bindableProperty = $bindable('fallback value') } = $props();
</script>
This fallback value only applies when the property is not bound. When the property is bound and a fallback value is present, the parent is expected to provide a value other than undefined
, else a runtime error is thrown. This prevents hard-to-reason-about situations where it's unclear which value should apply.