Forms
Form state and validation with zod, valibot or any Standard Schema library.
MarkdownUsage
import { z } from "zod";
const formRef = useTemplateRef("signup-form");
const { form, errors, submit, processing, isDirty, reset } = useForm({
schema: z.object({
email: z.email(),
password: z.string().min(8),
}),
initialValues: { email: "", password: "" },
formRef,
onSubmit: (values) => api.signUp(values),
mapError: (error) => (error instanceof ApiError ? error.fields : undefined),
});Copy codeGive each field a name and its error:
<form ref="signup-form" @submit="submit">
<UiInput v-model="form.email" name="email" label="Email" :error="errors.email" />
<UiButton type="submit" :loading="processing">Sign up</UiButton>
</form>Copy codeNested fields
Bind nested values as usual, and name each input by its path. Errors use the same path; array items use their index, like items.0.name.
<UiInput
v-model="form.address.city"
name="address.city"
label="City"
:error="errors['address.city']"
/>Copy codeOptions
| Option | Description |
|---|---|
schema | Any Standard Schema. Its output type is what onSubmit receives. |
initialValues | Starting values; also the baseline for isDirty and reset(). |
onSubmit | Called with validated values. Errors it throws go through mapError. |
mapError | Turns a thrown error into field errors, e.g. a 422 from your API. |
onError | Called with any thrown error. Without it, non-field errors rethrow. |
formRef | The <form>; enables blur/input tracking and focusing the first invalid field. |
validateOn | When a field's error appears: "blur" (default), "input" or "submit". |
Returns
| Key | Description |
|---|---|
form | Reactive values to bind with v-model. |
errors | Visible errors, keyed by field path ("address.city" for nested fields). |
submit | Validates, then calls onSubmit. Use as the form's @submit. |
processing / validating | Submit and validation in flight. |
isValid / isDirty | Schema validity, and whether values differ from the baseline. |
reset() / setValues() | Restore the baseline, or set new values as the baseline. |
setErrors() / touch() | Show server errors, or mark a field touched. |
Plain HTML forms
A field with a name is sent when a regular <form> submits, just like a native input. A multiple Select or Combobox sends one value per pick; a Switch that's off sends nothing.
<form method="post" action="/settings">
<UiSelect name="plan" v-model="plan" :options="plans" label="Plan" />
<UiSwitch name="newsletter" v-model="newsletter" label="Newsletter" />
<UiButton type="submit">Save</UiButton>
</form>Copy codeuseForm uses the same name to show a field's error once you leave it.