--- 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 ```svelte <!--- 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`. ```svelte <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. ```svelte <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`. ```svelte <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](https://developer.mozilla.org/en-US/docs/Web/API/FileList). 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`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) object and get `files` from there. ```svelte <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. ```svelte <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 `<select>` 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). ```svelte <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>`. ```svelte <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. ```svelte <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`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) - [`innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText) - [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) There are slight differences between each of these, read more about them [here](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#Differences_from_innerText). <!-- for some reason puts the comment and html on same line --> <!-- prettier-ignore --> ```svelte <div contenteditable="true" bind:innerHTML={html} /> ``` `<details>` elements support binding to the `open` property. ```svelte <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. ```svelte <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 ```svelte <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](http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/): - `clientWidth` - `clientHeight` - `offsetWidth` - `offsetHeight` ```svelte <div bind:offsetWidth={width} bind:offsetHeight={height}> <Chart {width} {height} /> </div> ``` ## bind:group ```svelte <!--- copy: false ---> bind:group={variable} ``` Inputs that work together can use `bind:group`. ```svelte <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 ```svelte <!--- copy: false ---> bind:this={dom_node} ``` To get a reference to a DOM node, use `bind:this`. ```svelte <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. ```svelte <!--- App.svelte ---> <ShoppingCart bind:this={cart} /> <button onclick={() => cart.empty()}> Empty shopping cart </button> ``` ```svelte <!--- 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 ```svelte bind:property={variable} ``` You can bind to component props using the same syntax as for elements. ```svelte <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: ```svelte <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: ```svelte <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.