# Forms

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

```svelte
<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:

```svelte
<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:

```svelte
<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](/docs/svelte/components/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:

```svelte
<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.
