Form-Bindings sind der häufigste Anwendungsfall für bind: und einer der größten Ergonomie-Vorteile von Svelte. Statt für jedes Eingabefeld value= und onchange= zu schreiben, reicht eine einzige Direktive. Dieser Artikel deckt alle Form-Bindings systematisch ab — Text, Number, Textarea, Select, Checkbox, Radio, Group und File-Inputs.

Text-Inputs und Textareas

svelte Text-Input
<script>
    let name = $state('');
</script>

<input type="text" bind:value={name} />
<p>Hallo, {name || '(noch nichts eingegeben)'}</p>

bind:value arbeitet identisch mit <textarea>:

svelte Textarea
<script>
    let bio = $state('');
</script>

<textarea bind:value={bio} rows="4" placeholder="Bio..."></textarea>
<p>{bio.length} Zeichen</p>

Number- und Range-Inputs

Bei type="number" und type="range" liefert bind:value automatisch eine Zahl statt eines Strings:

svelte Number-Input
<script>
    let age = $state(30);
</script>

<input type="number" bind:value={age} min="0" max="120" />
<p>{age + 1} (nächstes Jahr)</p>

age + 1 funktioniert ohne Konvertierung — der Wert ist tatsächlich number.

svelte Range-Slider
<script>
    let volume = $state(50);
</script>

<input type="range" bind:value={volume} min="0" max="100" />
<p>Lautstärke: {volume}%</p>

Bei leerem <input type="number"> ist der Wert null (nicht '').

Checkbox

Eine einzelne Checkbox bindest du an einen Boolean:

svelte Einzel-Checkbox
<script>
    let acceptTerms = $state(false);
</script>

<label>
    <input type="checkbox" bind:checked={acceptTerms} />
    Allgemeine Geschäftsbedingungen akzeptieren
</label>

<button disabled={!acceptTerms}>Weiter</button>

Indeterminate-Zustand

Für die dritte Checkbox-Stellung („teilweise selektiert”):

svelte Indeterminate
<script>
    let allSelected = $state(false);
    let someSelected = $state(true);
</script>

<input
    type="checkbox"
    bind:checked={allSelected}
    bind:indeterminate={someSelected}
/>

bind:indeterminate ist unabhängig von bind:checked — beide können gleichzeitig wahr sein.

Radio-Gruppe mit bind:group

Bei einer Gruppe von Radio-Buttons hilft bind:group — die Variable enthält den Wert der aktuell ausgewählten Option:

svelte Radio-Group
<script>
    let plan = $state('free');
</script>

<fieldset>
    <legend>Tarif</legend>

    <label>
        <input type="radio" bind:group={plan} value="free" />
        Kostenlos
    </label>

    <label>
        <input type="radio" bind:group={plan} value="pro" />
        Pro
    </label>

    <label>
        <input type="radio" bind:group={plan} value="team" />
        Team
    </label>
</fieldset>

<p>Gewählt: {plan}</p>

Wichtig: Alle Radios mit demselben bind:group gehören automatisch zur selben Gruppe — kein eigenes name-Attribut nötig.

Checkbox-Gruppe mit bind:group

Bei Checkboxen hält bind:group ein Array der ausgewählten Werte:

svelte Checkbox-Group
<script>
    let interests = $state([]);
</script>

<fieldset>
    <legend>Interessen</legend>

    <label>
        <input type="checkbox" bind:group={interests} value="sport" />
        Sport
    </label>

    <label>
        <input type="checkbox" bind:group={interests} value="musik" />
        Musik
    </label>

    <label>
        <input type="checkbox" bind:group={interests} value="kunst" />
        Kunst
    </label>
</fieldset>

<p>Auswahl: {interests.join(', ') || '(keine)'}</p>

interests enthält z. B. ['sport', 'kunst'] und aktualisiert sich automatisch bei jeder Änderung.

Select – einfach und mehrfach

Single-Select

svelte Select
<script>
    let country = $state('de');
</script>

<select bind:value={country}>
    <option value="de">Deutschland</option>
    <option value="at">Österreich</option>
    <option value="ch">Schweiz</option>
</select>

<p>Ausgewählt: {country}</p>

option-Werte können beliebige JavaScript-Werte sein — auch Objekte. Svelte vergleicht sie per Identität:

svelte Object-Values
<script>
    const countries = [
        { code: 'de', name: 'Deutschland' },
        { code: 'at', name: 'Österreich' },
    ];

    let selected = $state(countries[0]);
</script>

<select bind:value={selected}>
    {#each countries as country (country.code)}
        <option value={country}>{country.name}</option>
    {/each}
</select>

<p>Code: {selected.code}</p>

Multi-Select

Mit dem multiple-Attribut wird die Variable zu einem Array:

svelte Multi-Select
<script>
    let selected = $state([]);
</script>

<select multiple bind:value={selected}>
    <option value="rot">Rot</option>
    <option value="gruen">Grün</option>
    <option value="blau">Blau</option>
</select>

<p>Auswahl: {selected.join(', ')}</p>

File-Inputs mit bind:files

Datei-Inputs liefern eine FileList über bind:files:

svelte File-Upload
<script>
    let files = $state(null);

    function upload() {
        if (!files) return;
        for (const file of files) {
            console.log(file.name, file.size, file.type);
        }
    }
</script>

<input type="file" multiple bind:files />

{#if files}
    <ul>
        {#each Array.from(files) as file}
            <li>{file.name} ({(file.size / 1024).toFixed(1)} KB)</li>
        {/each}
    </ul>
    <button onclick={upload}>Hochladen</button>
{/if}

files ist eine FileList (kein Array). Für map/filter mit Array.from(files) konvertieren.

Bindings sind in beiden Richtungen aktiv: Eine Zuweisung files = null setzt das Eingabefeld zurück.

TypeScript: Form-Bindings sauber typisieren

svelte TypeScript-Beispiel
<script lang="ts">
    type Plan = 'free' | 'pro' | 'team';

    let name = $state('');
    let age = $state<number | null>(null);
    let plan = $state<Plan>('free');
    let interests = $state<string[]>([]);
    let files = $state<FileList | null>(null);
</script>

<input bind:value={name} />
<input type="number" bind:value={age} />

Achtung: <input type="number"> bei leerer Eingabe gibt null zurück — der Type sollte das abbilden (number | null).

Praxis-Beispiel: Anmeldeformular

svelte Signup-Form
<script>
    let form = $state({
        email: '',
        password: '',
        role: 'user',
        receivesNewsletter: false,
        interests: [],
    });

    let isValid = $derived(
        form.email.includes('@') &&
        form.password.length >= 8
    );

    function submit(event) {
        event.preventDefault();
        console.log(form);
    }
</script>

<form onsubmit={submit}>
    <label>
        E-Mail
        <input type="email" bind:value={form.email} required />
    </label>

    <label>
        Passwort
        <input type="password" bind:value={form.password} required />
    </label>

    <label>
        Rolle
        <select bind:value={form.role}>
            <option value="user">Standard</option>
            <option value="admin">Admin</option>
        </select>
    </label>

    <label>
        <input type="checkbox" bind:checked={form.receivesNewsletter} />
        Newsletter abonnieren
    </label>

    <fieldset>
        <legend>Interessen</legend>
        <label>
            <input type="checkbox" bind:group={form.interests} value="dev" />
            Entwicklung
        </label>
        <label>
            <input type="checkbox" bind:group={form.interests} value="design" />
            Design
        </label>
    </fieldset>

    <button type="submit" disabled={!isValid}>Registrieren</button>
</form>

Alle Eingaben werden automatisch in form synchronisiert. Validierung und Disabled-Zustand laufen über $derived.

Häufige Stolperfallen

bind:value mit value="..." mischen. Beides gleichzeitig führt zu undefiniertem Verhalten. Bei bind: schreibt Svelte selbst — kein zusätzliches value=.

Numerischer Input mit String-Erwartung. bind:value bei type="number" liefert eine Zahl. Bei type="text" einen String — auch wenn der Nutzer Ziffern eingibt.

bind:group mit Variable außerhalb der <fieldset>. Funktioniert, ist aber semantisch unklar. Lieber Form-State zentral in einem Objekt halten und Felder gruppieren.

Datei-Input nicht zurücksetzen. Manche Browser akzeptieren files = null nicht zuverlässig; in dem Fall <input type="file" {key}> mit wechselndem key als Reset-Trigger nutzen.

Konflikt von bind:checked und bind:group auf demselben Input. Pro Input nur eine der beiden — sonst ist das Verhalten unklar.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Bindings

Zur Übersicht