Formulare sind in fast jeder Anwendung. Svelte hat dafür kein eigenes „Forms-Framework” — du arbeitest mit ganz normalem HTML, ergänzt um Sveltes Bindings und Reaktivität. Das fühlt sich erfrischend bodenständig an, sobald man die Patterns kennt. Dieser Artikel ordnet ein: Wie sieht ein Standard-Formular aus, was ist der Unterschied zwischen controlled und uncontrolled, wie behandelt man Submit und Reset, und wann ist eine SvelteKit Form Action das deutlich passendere Werkzeug.

Das einfachste Formular

svelte LoginForm.svelte
<script>
    let email = $state('');
    let password = $state('');

    function handleSubmit(event) {
        event.preventDefault();
        console.log({ email, password });
    }
</script>

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

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

    <button type="submit">Einloggen</button>
</form>

Drei Dinge zur Beachtung:

  1. bind:value={...} synchronisiert das Eingabefeld mit der Variable. Tippt der Nutzer, ändert sich email. Setzt der Code email = '', wird das Feld geleert.
  2. event.preventDefault() verhindert die klassische Form-Submission (mit Page-Reload). Ohne diesen Aufruf würde der Browser zur action-URL navigieren — was bei einer SPA nicht gewünscht ist.
  3. required und Co. sind native HTML-Validierungs-Attribute. Der Browser prüft sie, bevor onsubmit überhaupt feuert. Ein leeres Feld zeigt direkt eine Fehlermeldung.

Das ist der Kern: HTML-Formulare bleiben HTML-Formulare. Svelte fügt nur die Reaktivität dazu.

Controlled vs. uncontrolled — was meint das?

Diese Begriffe kommen aus der React-Welt, beschreiben aber ein generelles Pattern: Wer hält den Wert eines Eingabefeldes — der Browser oder dein Code?

Controlled heißt: Dein Code hält den Wert in einer Variable. Das Eingabefeld zeigt nur an, was die Variable sagt. Bei jedem Tastendruck wird die Variable aktualisiert. Der Klassiker ist ein bind:value-Feld.

Uncontrolled heißt: Der Browser verwaltet den Wert. Dein Code liest ihn erst beim Submit aus dem DOM aus. Du nutzt keine Bindings.

svelte Uncontrolled-Variante
<script>
    function handleSubmit(event) {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        console.log(Object.fromEntries(formData));
    }
</script>

<form onsubmit={handleSubmit}>
    <input type="email" name="email" required />
    <input type="password" name="password" required />
    <button type="submit">Senden</button>
</form>

Bei der uncontrolled-Variante ist der Code überraschend kurz — keine $state-Variablen, keine Bindings. Beim Submit liest die FormData-API alle benannten Felder aus dem DOM.

Wann was?

SituationEmpfehlung
Live-Validation während des TippensControlled
Passwort-Stärke-Anzeige, Live-VorschauControlled
Felder voneinander abhängig (z. B. Auto-Slug aus Titel)Controlled
Einfaches Submit-Formular ohne Live-LogikUncontrolled
Server-seitige Verarbeitung mit FormDataUncontrolled
SvelteKit Form ActionsUncontrolled

In Svelte ist die Wahl oft uncontrolled — weil die HTML-Form-Mechanik schon viel mitbringt und sauber zu Form Actions passt.

Reset, Disabled, Submit-Status

Ein paar Patterns, die in fast jedem Formular vorkommen.

Submit-Status anzeigen

Ein Submit-Button sollte deaktiviert sein, solange das Formular läuft — sonst löst der Nutzer das Submit zweimal aus.

svelte Mit Submit-Status
<script>
    let email = $state('');
    let saving = $state(false);

    async function handleSubmit(event) {
        event.preventDefault();
        saving = true;
        try {
            await fetch('/api/login', {
                method: 'POST',
                body: JSON.stringify({ email }),
            });
        } finally {
            saving = false;
        }
    }
</script>

<form onsubmit={handleSubmit}>
    <input bind:value={email} disabled={saving} />
    <button type="submit" disabled={saving}>
        {saving ? 'Sende ...' : 'Einloggen'}
    </button>
</form>

Drei Punkte: saving deaktiviert den Button und das Eingabefeld, der Button-Text wechselt, und der try/finally-Block stellt sicher, dass saving auch im Fehlerfall zurückgesetzt wird.

Reset-Funktionalität

Ein Reset-Button kann entweder per HTML (<button type="reset">) oder per Code passieren:

svelte Code-basierter Reset
<script>
    let email = $state('');
    let password = $state('');

    function reset() {
        email = '';
        password = '';
    }
</script>

<form>
    <input bind:value={email} />
    <input bind:value={password} type="password" />
    <button type="button" onclick={reset}>Zurücksetzen</button>
</form>

type="button" ist wichtig — ohne dieses Attribut wäre der Button standardmäßig ein Submit-Button und würde das Formular abschicken statt es zurückzusetzen.

Tasten-Shortcuts: Enter und Esc

Browser-Default für Formulare: Enter in einem Eingabefeld löst Submit aus, sofern es einen Submit-Button gibt. Das ist eine Funktion, die viele Anfänger übersehen — und die du nicht selbst nachbauen musst.

Was du selbst ergänzen kannst: Escape zum Schließen oder Zurücksetzen.

svelte Escape-Reset
<script>
    let query = $state('');

    function handleKey(event) {
        if (event.key === 'Escape') query = '';
    }
</script>

<input bind:value={query} onkeydown={handleKey} />

Native HTML-Validierung — was sie schon kann

Bevor man Validierungs-Libraries einzieht, lohnt der Blick auf das, was HTML schon mitbringt:

AttributWirkung
requiredFeld darf nicht leer sein
minlength / maxlengthMin/Max-Zeichenzahl
min / maxMin/Max-Wert (Number, Date)
patternRegex-Match
type="email"Eingabe muss eine syntaktisch gültige E-Mail sein
type="url"Gültige URL
stepErlaubte Schritt-Werte bei Number-/Date-Feldern
inputmodeWelches mobile Keyboard angezeigt wird

Diese Validierung läuft clientseitig und blockiert den Submit, wenn ein Feld nicht passt. Der Browser zeigt eigene Fehlermeldungen, oft auf der Sprache des Nutzers. Für viele Formulare reicht das vollkommen aus — du brauchst keine eigene Logik.

Sobald komplexere Regeln nötig sind (zwei Felder müssen zueinander passen, ein Username darf nicht vergeben sein), kommt entweder eigene JavaScript-Logik dazu oder eine Validierungs-Library wie Zod. Mehr dazu im nächsten Artikel.

Wann SvelteKit Form Actions die bessere Wahl sind

Bisher haben wir Formulare client-seitig per fetch abgeschickt. Wer SvelteKit nutzt, hat eine Alternative: Form Actions. Statt event.preventDefault() und Fetch leitet das Formular einfach an eine Server-Route, die den Submit verarbeitet.

svelte src/routes/login/+page.svelte
<form method="POST" action="?/login">
    <input name="email" type="email" required />
    <input name="password" type="password" required />
    <button>Einloggen</button>
</form>
ts src/routes/login/+page.server.ts
export const actions = {
    login: async ({ request }) => {
        const data = await request.formData();
        const email = data.get('email');
        const password = data.get('password');
        // ...prüfen, einloggen
        return { success: true };
    },
};

Die Stärke dieses Ansatzes: Er funktioniert auch ohne JavaScript. Der Browser submitted das Formular klassisch, der Server antwortet, die Seite wird neu gerendert. Mit JavaScript dazu (per enhance) wird das ganze ohne Reload abgewickelt — das beste aus beiden Welten.

Vertieft im Artikel SvelteKit Form Actions.

Häufige Stolperfallen

button ohne type löst Submit aus. Im <form>-Kontext ist <button> standardmäßig type="submit". Ein Reset- oder „Mehr-Optionen-anzeigen”-Button braucht explizit type="button".

event.preventDefault() vergessen. Folge: Beim Submit reloaded die Seite, alle State-Werte gehen verloren. Bei controlled-Forms mit Fetch immer preventDefault rufen.

bind:value und value= gleichzeitig. Macht das Verhalten undefiniert. Entscheide dich für eines.

Submit-Button im Lade-Zustand vergessen. Ohne disabled={saving} kann der Nutzer den Submit mehrfach auslösen — und du hast Race-Conditions. Immer disablen.

Native Validation vorschnell abschalten. Mit novalidate am <form> deaktivierst du die Browser-Validierung. Tu das nur, wenn du wirklich eine eigene Validierungs-Pipeline einziehst.

Form-Reset trifft $state-Variablen nicht. Ein klassisches <button type="reset"> resettet die DOM-Felder auf ihre value-Attribute, nicht auf deine $state-Variablen. Wer die State zurücksetzen will, schreibt einen Reset-Handler im Code.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Forms

Zur Übersicht