useForm
v0.0.16testeddemoHeadless, performant form state management. Holds reactive `values`, flat path-keyed `errors`/touched maps, derived `meta`, and a full set of mutation/validation/submit/reset helpers. Validation accepts a [Standard Schema](https://github.com/standard-schema/standard-schema) (zod/valibot/arktype), a custom resolver, or per-field function validators.
Examples
const { values, errors, handleSubmit } = useForm({
initialValues: { email: '', age: 0 },
schema: z.object({ email: z.string().email(), age: z.number().min(18) }),
});
const onSubmit = handleSubmit((output) => save(output));// Inline binding with defineField
const form = useForm({ initialValues: { name: '' } });
const [name, nameProps] = form.defineField('name');
// <input v-model="name" v-bind="nameProps">Demo
Signature
export function useForm<TInput extends object, TOutput = TInput>(
options: UseFormOptions<TInput, TOutput> ={ ... }Type Parameters
TInputextends objectTOutput= TInputParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
options? | UseFormOptions<TInput, TOutput> | {} | Initial values, schema/resolver, and validation triggers |
Returns
UseFormReturn<TInput, TOutput>The reactive form instance (also provided to descendant fields)Related Types
FormResolverResult
The shape returned by a {@link FormResolver}.
interface FormResolverResult<TOutput>| Property | Type | Default | Description |
|---|---|---|---|
values? | TOutput | undefined | — | The (optionally transformed) valid output. Read on success. |
errors? | FormErrors | undefined | — | Flat map of path → messages. Empty/absent means valid. |
FormValidationResult
The outcome of running form validation.
interface FormValidationResult<TOutput>| Property | Type | Default | Description |
|---|---|---|---|
valid | boolean | — | Whether the form is valid (no errors). |
errors | FormErrors | — | The flat error map produced by this run. |
output? | TOutput | undefined | — | The typed, validated output (present only when `valid`). |
FieldValidationResultDetail
The outcome of validating a single field.
interface FieldValidationResultDetail| Property | Type | Default | Description |
|---|---|---|---|
valid | boolean | — | Whether the field is valid. |
errors | string[] | — | The field's error messages (empty when valid). |
FormMeta
Reactive meta flags describing the whole form.
interface FormMeta| Property | Type | Default | Description |
|---|---|---|---|
dirty | boolean | — | Whether any value differs from its initial snapshot. |
valid | boolean | — | Whether the form currently has no errors. |
touched | boolean | — | Whether any field has been touched. |
pending | boolean | — | Whether a validation run is in flight. |
FieldMeta
Reactive meta flags describing a single field.
interface FieldMeta| Property | Type | Default | Description |
|---|---|---|---|
dirty | ComputedRef<boolean> | — | Whether the field's value differs from its initial snapshot. |
touched | ComputedRef<boolean> | — | Whether the field has been touched (blurred). |
valid | ComputedRef<boolean> | — | Whether the field currently has no errors. |
FieldBindingProps
Props to spread onto a native field element (via `v-bind`).
interface FieldBindingProps| Property | Type | Default | Description |
|---|---|---|---|
name | string | — | The field's dotted path, suitable as the input `name`. |
onBlur | (event?: Event) => void | — | Blur handler that marks the field touched and (re)validates per trigger. |
'aria-invalid' | boolean | undefined | — | `true` when the field currently has errors, for `aria-invalid`. |
SetValueOptions
Options for {@link UseFormOptions}'s value-write helpers.
interface SetValueOptions| Property | Type | Default | Description |
|---|---|---|---|
shouldValidate? | boolean | undefined | — | Whether to (re)validate after the write. Defaults to the form's trigger config. |
shouldTouch? | boolean | undefined | — | Whether to mark the field touched. Defaults to `false`. |
FormResetState
State accepted by `resetForm`.
interface FormResetState<TInput extends object>| Property | Type | Default | Description |
|---|---|---|---|
values? | PartialDeep<TInput> | undefined | — | New baseline values (defaults to the original initial values). |
touched? | Record<string, boolean> | undefined | — | New touched map (defaults to empty). |
errors? | FormErrors | undefined | — | New error map (defaults to empty). |
UseFormOptions
Options for {@link useForm}.
interface UseFormOptions<TInput extends object, TOutput = TInput>| Property | Type | Default | Description |
|---|---|---|---|
initialValues? | MaybeRefOrGetter<PartialDeep<TInput>> | undefined | — | Initial form values (ref/getter supported; cloned on init). |
schema? | any | — | A Standard Schema (zod/valibot/arktype/…) validating the whole form. |
resolver? | FormResolver<TInput, TOutput> | undefined | — | A custom form-level resolver (alternative to `schema`). |
validateOnMount? | boolean | undefined | false | Validate once on mount. |
validateOn? | ValidationTrigger | undefined | 'submit' | When to validate before the first submit. |
revalidateOn? | ValidationTrigger | undefined | 'value' | When to validate after the first submit (or after a field is touched). |
FormContext
The form instance returned by {@link useForm} (and injected by {@link useFormContext}). Also serves as the context shared with fields.
interface FormContext<TInput extends object = any, TOutput = TInput>| Property | Type | Default | Description |
|---|---|---|---|
values | TInput | — | Reactive form values. Bind directly with `v-model="values.path"`. |
errors | ComputedRef<FormErrors> | — | Flat, reactive map of path → error messages. |
meta | ComputedRef<FormMeta> | — | Grouped reactive meta flags for the whole form. |
isDirty | ComputedRef<boolean> | — | Whether any value differs from the initial snapshot. |
isValid | ComputedRef<boolean> | — | Whether the form has no errors. |
isValidating | Readonly<Ref<boolean, boolean>> | — | Whether a validation run is in flight. |
isSubmitting | Readonly<Ref<boolean, boolean>> | — | Whether a submit is in flight. |
submitCount | Readonly<Ref<number, number>> | — | Number of times submit has been attempted. |
getFieldValue | <P extends FieldPath<TInput>>(path: P) => FieldPathValue<TInput, P> | — | Read a field value by path. |
getError | (path: FieldPath<TInput>) => string | undefined | — | The first error message for a path, if any. |
getErrors | (path: FieldPath<TInput>) => string[] | — | All error messages for a path (empty array when none). |
isFieldDirty | (path: FieldPath<TInput>) => boolean | — | Whether a field differs from its initial snapshot. |
isFieldTouched | (path: FieldPath<TInput>) => boolean | — | Whether a field has been touched. |
isFieldValid | (path: FieldPath<TInput>) => boolean | — | Whether a field currently has no errors. |
setFieldValue | <P extends FieldPath<TInput>>(path: P, value: FieldPathValue<TInput, P>, options?: SetValueOptions) => void | — | Write a field value by path. |
setValues | (values: PartialDeep<TInput>, options?: { merge?: boolean; }) => void | — | Merge or replace multiple values at once. |
setFieldError | (path: FieldPath<TInput>, message: string | string[] | null) => void | — | Set or clear (with `null`) a field's error messages. |
setErrors | (errors: FormErrors) => void | — | Replace the entire error map. |
setFieldTouched | (path: FieldPath<TInput>, touched?: boolean) => void | — | Mark a field touched/untouched. |
setTouched | (touched?: boolean) => void | — | Mark all known fields touched/untouched. |
validate | () => Promise<FormValidationResult<TOutput>> | — | Validate the whole form. |
validateField | (path: FieldPath<TInput>) => Promise<FieldValidationResultDetail> | — | Validate a single field (runs the pipeline, updates that field's errors). |
resetForm | (state?: FormResetState<TInput>) => void | — | Reset the form to its initial (or provided) state. |
resetField | <P extends FieldPath<TInput>>(path: P, value?: FieldPathValue<TInput, P>) => void | — | Reset a single field to its initial (or provided) value. |
handleSubmit | (onValid: SubmissionHandler<TOutput>, onInvalid?: InvalidSubmissionHandler) => (event?: Event) => Promise<void> | — | Wrap a submit callback: validates, then calls `onValid` with typed output (or `onInvalid` with the error map). |
handleReset | (event?: Event) => void | — | Reset handler suitable for a `<form>`'s `reset` event. |
defineField | <P extends FieldPath<TInput>>(path: P, options?: DefineFieldOptions<FieldPathValue<TInput, P>, TInput>) => [Ref<FieldPathValue<TInput, P>>, ComputedRef<FieldBindingProps>] | — | Bind a field inline: returns `[model, props]` for `v-model` + `v-bind`. |
formProps | { onSubmit: (event: Event) => void; onReset: (event: Event) => void; novalidate: boolean; } | — | Props to spread on the `<form>` element (`@submit`/`@reset`/`novalidate`). |
_registerValidator | (path: string, validator: FieldValidator) => void | — | — |
_unregisterValidator | (path: string, validator: FieldValidator) => void | — | — |
_shouldValidate | (trigger: Exclude<ValidationTrigger, "manual">) => boolean | — | — |
_remapFieldPaths | (basePath: string, indexMap: (index: number) => number | null) => void | — | — |
DefineFieldOptions
Options for `defineField`.
interface DefineFieldOptions<T = any, TInput extends object = any>| Property | Type | Default | Description |
|---|---|---|---|
validate? | FieldValidator<T, TInput> | undefined | — | A field-level function validator. |
validateOn? | ValidationTrigger | undefined | — | Override the form's validation trigger for this field. |
UseFieldOptions
Options for {@link useField}.
interface UseFieldOptions<T, TInput extends object = any>| Property | Type | Default | Description |
|---|---|---|---|
form? | FormContext<TInput, TInput> | undefined | — | The form to bind to. Defaults to the injected form context; if there is none and `initialValue` is given, the field runs standalone. |
initialValue? | T | undefined | — | Initial value for standalone mode (no form context). |
validate? | FieldValidator<T, TInput> | undefined | — | A field-level function validator. |
schema? | any | — | A per-field Standard Schema (standalone or augmenting the form schema). |
validateOn? | ValidationTrigger | undefined | — | Override the form's validation trigger for this field. |
UseFieldReturn
The reactive API returned by {@link useField}.
interface UseFieldReturn<T>| Property | Type | Default | Description |
|---|---|---|---|
value | Ref<T, T> | — | The field's writable value (bound to the form path, or local in standalone). |
errors | ComputedRef<string[]> | — | The field's error messages. |
errorMessage | ComputedRef<string | undefined> | — | The field's first error message, if any. |
meta | FieldMeta | — | Grouped reactive meta for the field. |
handleBlur | (event?: Event) => void | — | Blur handler — marks touched and (re)validates per trigger. |
handleChange | (value: T, shouldValidate?: boolean) => void | — | Set the value and optionally validate. |
handleInput | (event: Event) => void | — | `input`/`change` DOM handler for native elements. |
setValue | (value: T) => void | — | Set the field value programmatically. |
setTouched | (touched?: boolean) => void | — | Mark the field touched/untouched. |
setErrors | (message: string | string[] | null) => void | — | Set or clear (with `null`) the field's errors. |
validate | () => Promise<FieldValidationResultDetail> | — | Validate just this field. |
reset | () => void | — | Reset the field to its initial value. |
attrs | ComputedRef<FieldBindingProps> | — | Props to spread on the field element (`v-bind="attrs"`). |
FieldArrayEntry
One entry of a {@link useFieldArray}.
interface FieldArrayEntry<T>| Property | Type | Default | Description |
|---|---|---|---|
key | number | — | A stable key for `v-for`, preserved across reorders. |
value | Ref<T, T> | — | The item's writable value (bound to the array slot). |
isFirst | boolean | — | Whether this is the first entry. |
isLast | boolean | — | Whether this is the last entry. |
UseFieldArrayReturn
The API returned by {@link useFieldArray}.
interface UseFieldArrayReturn<T>| Property | Type | Default | Description |
|---|---|---|---|
fields | Readonly<Ref<FieldArrayEntry<T>[], FieldArrayEntry<T>[]>> | — | The reactive list of entries with stable keys. |
push | (value: T) => void | — | Append an item. |
prepend | (value: T) => void | — | Prepend an item. |
insert | (index: number, value: T) => void | — | Insert an item at an index. |
remove | (index: number) => void | — | Remove the item at an index. |
move | (from: number, to: number) => void | — | Move an item from one index to another. |
swap | (indexA: number, indexB: number) => void | — | Swap two items. |
replace | (values: T[]) => void | — | Replace the whole array. |
update | (index: number, value: T) => void | — | Replace a single item. |
UseFieldArrayOptions
Options for {@link useFieldArray}.
interface UseFieldArrayOptions<TInput extends object = any>| Property | Type | Default | Description |
|---|---|---|---|
form? | FormContext<TInput, TInput> | undefined | — | The form to bind to. Defaults to the injected form context. |
FieldPath
Union of every dot-separated path into `T` (including array indices), e.g. `'email' | 'address' | 'address.city' | 'tags.0'`.
export type FieldPath<T> = T extends FieldPrimitive
? never
: T extends ReadonlyArray<infer U>
? `${number}` | `${number}.${FieldPath<U>}`
: {
[K in keyof T & (string | number)]: T[K] extends FieldPrimitive
? `${K}`
: `${K}` | `${K}.${FieldPath<T[K]>}`;
}[keyof T & (string | number)];FieldPathValue
The value type at a given {@link FieldPath} of `T`.
export type FieldPathValue<T, P extends string> = P extends `${infer K}.${infer Rest}`
? K extends keyof T
? FieldPathValue<T[K], Rest>
: T extends ReadonlyArray<infer U>
? FieldPathValue<U, Rest>
: unknown
: P extends keyof T
? T[P]
: T extends ReadonlyArray<infer U>
? U
: unknown;PartialDeep
A recursively-partial version of `T` (used for `initialValues`/`setValues`).
export type PartialDeep<T> = T extends FieldPrimitive
? T
: T extends ReadonlyArray<infer U>
? Array<PartialDeep<U>>
: { [K in keyof T]?: PartialDeep<T[K]> };ValidationTrigger
When validation runs for a field/form. - `value` — on every value change - `blur` — when the field is blurred - `submit` — only on submit - `manual` — never automatically; only via `validate()`/`validateField()`
export type ValidationTrigger = 'value' | 'blur' | 'submit' | 'manual';FormErrors
Flat map of field path → error messages.
export type FormErrors = Record<string, string[]>;FieldValidationResult
The value a field-level function validator may return. A `string`/`string[]` is treated as error message(s); `true`/`void`/`null`/`undefined` means valid.
export type FieldValidationResult = string | string[] | true | void | null | undefined;FieldValidator
A field-level function validator.
export type FieldValidator<T = any, TInput extends object = any>
= (value: T, values: TInput) => FieldValidationResult | Promise<FieldValidationResult>;FormResolver
A custom form-level resolver (alternative to a Standard Schema).
export type FormResolver<TInput extends object, TOutput = TInput>
= (values: TInput) => FormResolverResult<TOutput> | Promise<FormResolverResult<TOutput>>;SubmissionHandler
The success callback for `handleSubmit`.
export type SubmissionHandler<TOutput> = (values: TOutput, event?: Event) => void | Promise<void>;InvalidSubmissionHandler
The invalid callback for `handleSubmit`.
export type InvalidSubmissionHandler = (errors: FormErrors, event?: Event) => void;UseFormReturn
Alias: the public return of {@link useForm} is the form context itself.
export type UseFormReturn<TInput extends object = any, TOutput = TInput> = FormContext<TInput, TOutput>;