svelte/documentation/docs/02-template-syntax/08-bindings.md

9.5 KiB

title
Bindings
  • how for dom elements
  • list of all bindings
  • how for components

Most of the time a clear separation between data flowing down and events going up is worthwhile and results in more robust apps. But in some cases - especially when interacting with form elements - it's more ergonomic to declare a two way binding. Svelte provides many element bindings out of the box, and also allows component bindings.

bind:property for elements

<!--- copy: false --->
bind:property={variable}

Data ordinarily flows down, from parent to child. The bind: directive allows data to flow the other way, from child to parent. Most bindings are specific to particular elements.

The simplest bindings reflect the value of a property, such as input.value.

<input bind:value={name} />
<textarea bind:value={text} />

<input type="checkbox" bind:checked={yes} />

If the name matches the value, you can use a shorthand.

<input bind:value />
<!-- equivalent to
<input bind:value={value} />
-->

Numeric input values are coerced; even though input.value is a string as far as the DOM is concerned, Svelte will treat it as a number. If the input is empty or invalid (in the case of type="number"), the value is undefined.

<input type="number" bind:value={num} />
<input type="range" bind:value={num} />

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.

DataTransfer may not be available in server-side JS runtimes. Leaving the state that is bound to files uninitialized prevents potential errors if components are server-side rendered.

If you're using bind: directives together with on event attributes, the binding will always fire before the event attribute.

<script>
	let value = 'Hello World';
</script>

<input oninput={() => console.log('New value:', value)} bind:value />

Here we were binding to the value of a text input, which uses the input event. Bindings on other elements may use different events such as change.

Binding 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> Elements with the contenteditable attribute support the following bindings: innerHTML innerText textContent There are slight differences between each of these, read more about them here. <div contenteditable="true" bind:innerHTML={html} /> <details> elements support binding to the open property. <details bind:open={isOpen}> <summary>Details</summary> <p>Something small enough to escape casual notice.</p> </details> Media element bindings Media elements (<audio> and <video>) have their own set of bindings — seven readonly ones... duration (readonly) — the total duration of the video, in seconds buffered (readonly) — an array of {start, end} objects played (readonly) — ditto seekable (readonly) — ditto seeking (readonly) — boolean ended (readonly) — boolean readyState (readonly) — number between (and including) 0 and 4 ...and five two-way bindings: currentTime — the current playback time in the video, in seconds playbackRate — how fast or slow to play the video, where 1 is 'normal' paused — this one should be self-explanatory volume — a value between 0 and 1 muted — a boolean value indicating whether the player is muted Videos additionally have readonly videoWidth and videoHeight bindings. <video src={clip} bind:duration bind:buffered bind:played bind:seekable bind:seeking bind:ended bind:readyState bind:currentTime bind:playbackRate bind:paused bind:volume bind:muted bind:videoWidth bind:videoHeight /> Image element bindings Image elements (<img>) have two readonly bindings: naturalWidth (readonly) — the original width of the image, available after the image has loaded naturalHeight (readonly) — the original height of the image, available after the image has loaded <img bind:naturalWidth bind:naturalHeight ></img> Block-level element bindings Block-level elements have 4 read-only bindings, measured using a technique similar to this one: clientWidth clientHeight offsetWidth offsetHeight <div bind:offsetWidth={width} bind:offsetHeight={height}> <Chart {width} {height} /> </div> bind:group <!--- copy: false ---> bind:group={variable} Inputs that work together can use bind:group. <script> let tortilla = 'Plain'; /** @type {Array<string>} */ let fillings = []; </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)" /> bind:group only works if the inputs are in the same Svelte component. bind:this <!--- copy: false ---> bind:this={dom_node} To get a reference to a DOM node, use bind:this. <script> import { onMount } from 'svelte'; /** @type {HTMLCanvasElement} */ let canvasElement; onMount(() => { const ctx = canvasElement.getContext('2d'); drawStuff(ctx); }); </script> <canvas bind:this={canvasElement} /> Components also support bind:this, allowing you to interact with component instances programmatically. <!--- App.svelte ---> <ShoppingCart bind:this={cart} /> <button onclick={() => cart.empty()}> Empty shopping cart </button> <!--- ShoppingCart.svelte ---> <script> // All instance exports are available on the instance object export function empty() { // ... } </script> Note that we can't do {cart.empty} since cart is undefined when the button is first rendered and throws an error. 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.