Submit component values, label controls, and show validation errors.

Give a control a name to include its value in a form. This works for Select and Combobox as well as inputs and checkboxes.

Submit a Value

<script lang="ts">
  import { Checkbox } from "@ui-sdk/svelte/checkbox"
</script>

<form action="/preferences" method="post">
  <label
    ><Checkbox name="updates" value="yes" defaultChecked /> Email me updates</label
  >
  <button type="submit">Save</button>
  <button type="reset">Reset</button>
</form>

The checkbox submits updates=yes when checked. Reset restores its default value.

Reset a Controlled Value

A reset also goes through onCheckedChange and bind:checked. Keep the binding in place so your state follows the reset:

<script lang="ts">
  import { Checkbox, type CheckedState } from "@ui-sdk/svelte/checkbox"

  let checked = $state<CheckedState>(true)
  let reason = $state("No change yet")
</script>

<form>
  <label>
    <Checkbox
      bind:checked
      name="updates"
      defaultChecked
      onCheckedChange={(_next, details) => (reason = details.reason)}
    />
    Email me updates
  </label>
  <button type="reset">Reset</button>
  <output>{reason}</output>
</form>

Uncheck the box, then press Reset. defaultChecked supplies the reset value. The callback receives true with reason "reset", and the binding updates checked. The callback runs when reset requests a different value; it does not run if the checkbox is already at its default.

Label and Validate a Field

Field connects a control to its label, description, and error message:

<script lang="ts">
  import {
    Field,
    FieldDescription,
    FieldError,
    FieldLabel,
  } from "@ui-sdk/svelte/field"
  import { Input } from "@ui-sdk/svelte/input"
</script>

<Field name="email">
  <FieldLabel>Email</FieldLabel>
  <Input type="email" required />
  <FieldDescription>We'll only use it for receipts.</FieldDescription>
  <FieldError />
</Field>

You can use a Textarea, Checkbox, Switch, Radio Group, or Select in place of the Input. Field shows how they connect.

On controls that support them, required, disabled, readOnly, and form follow native conventions. Disabled fields are left out of submission; read-only fields remain focusable and submitted.

Handle a Submission

On a normal submit, the browser checks constraints such as required and type="email" before running your handler.

Your server may still reject a valid-looking value. Pass invalid to the field and show the server’s message. When the user edits the field, clear the message and return invalid to undefined so the field uses native validation again. This example expects a /api/preferences endpoint:

<script lang="ts">
  import { Field, FieldError, FieldLabel } from "@ui-sdk/svelte/field"
  import { Input } from "@ui-sdk/svelte/input"

  let error = $state("")

  async function save(event: SubmitEvent) {
    event.preventDefault()
    const body = new FormData(event.currentTarget as HTMLFormElement)
    const response = await fetch("/api/preferences", { method: "POST", body })
    error = response.ok ? "" : "That email is already taken."
  }
</script>

<form onsubmit={save}>
  <Field name="email" invalid={error ? true : undefined}>
    <FieldLabel>Email</FieldLabel>
    <Input type="email" required onValueChange={() => (error = "")} />
    <FieldError>
      {#snippet children(state)}{error || state.message}{/snippet}
    </FieldError>
  </Field>
  <button type="submit">Save</button>
</form>

Without a handler, the form submits through its action URL.