humanonlyweb/ui

Forms

Form state and validation with zod, valibot or any Standard Schema library.

Markdown

At least 8 characters.

Copy code

Usage

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 code

Give 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 code

Nested 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.

Copy code
<UiInput
  v-model="form.address.city"
  name="address.city"
  label="City"
  :error="errors['address.city']"
/>
Copy code

Options

OptionDescription
schemaAny Standard Schema. Its output type is what onSubmit receives.
initialValuesStarting values; also the baseline for isDirty and reset().
onSubmitCalled with validated values. Errors it throws go through mapError.
mapErrorTurns a thrown error into field errors, e.g. a 422 from your API.
onErrorCalled with any thrown error. Without it, non-field errors rethrow.
formRefThe <form>; enables blur/input tracking and focusing the first invalid field.
validateOnWhen a field's error appears: "blur" (default), "input" or "submit".

Returns

KeyDescription
formReactive values to bind with v-model.
errorsVisible errors, keyed by field path ("address.city" for nested fields).
submitValidates, then calls onSubmit. Use as the form's @submit.
processing / validatingSubmit and validation in flight.
isValid / isDirtySchema 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 code

useForm uses the same name to show a field's error once you leave it.