Painless forms for Vue.js with comprehensive validation, composition API, and component-based approaches.
—
Composables for accessing reactive form and field state information. These composables provide read-only access to various aspects of form and field state for reactive UI updates and conditional logic.
Gets reactive access to a field's current value.
/**
* Gets reactive access to a field's current value
* @param path - Field path (uses injected field context if omitted)
* @returns Computed ref to field value
*/
function useFieldValue<TValue = unknown>(path?: MaybeRefOrGetter<string>): ComputedRef<TValue | undefined>;Gets reactive access to a field's error message.
/**
* Gets reactive access to a field's error message
* @param path - Field path (uses injected field context if omitted)
* @returns Computed ref to field error message
*/
function useFieldError(path?: MaybeRefOrGetter<string>): ComputedRef<string | undefined>;Field State Examples:
import { useFieldValue, useFieldError, useField } from "vee-validate";
// Using with explicit path
const email = useFieldValue('email');
const emailError = useFieldError('email');
// Using within field context (path omitted)
const EmailField = {
setup() {
const field = useField('email', 'required|email');
// These will automatically use the 'email' field context
const value = useFieldValue(); // Same as field.value
const error = useFieldError(); // Same as field.errorMessage
return { value, error };
}
};
// Reactive field path
const currentField = ref('username');
const fieldValue = useFieldValue(currentField);
const fieldError = useFieldError(currentField);
// Watch field changes
watchEffect(() => {
console.log(`${currentField.value} value:`, fieldValue.value);
if (fieldError.value) {
console.log(`${currentField.value} error:`, fieldError.value);
}
});
// Computed properties based on field state
const isEmailValid = computed(() => {
const emailVal = useFieldValue('email');
const emailErr = useFieldError('email');
return emailVal.value && !emailErr.value;
});Gets reactive access to all form values.
/**
* Gets reactive access to all form values
* @returns Computed ref to form values object
*/
function useFormValues<TValues extends GenericObject = GenericObject>(): ComputedRef<Partial<TValues>>;Gets reactive access to all form errors.
/**
* Gets reactive access to all form errors
* @returns Computed ref to form errors object
*/
function useFormErrors<TValues extends GenericObject = GenericObject>(): ComputedRef<FormErrors<TValues>>;
type FormErrors<TValues extends GenericObject> = Partial<Record<Path<TValues> | '', string | undefined>>;Form State Examples:
import { useFormValues, useFormErrors, useForm } from "vee-validate";
// Setup form context
const { handleSubmit } = useForm();
// Access form state
const formValues = useFormValues();
const formErrors = useFormErrors();
// Watch form changes
watchEffect(() => {
console.log('Current form values:', formValues.value);
console.log('Current form errors:', formErrors.value);
});
// Computed properties based on form state
const hasErrors = computed(() => {
return Object.keys(formErrors.value).length > 0;
});
const formDataSummary = computed(() => {
const values = formValues.value;
return {
fieldCount: Object.keys(values).length,
filledFields: Object.values(values).filter(v => v !== '' && v != null).length,
hasData: Object.values(values).some(v => v !== '' && v != null)
};
});
// Form validation summary
const validationSummary = computed(() => {
const errors = formErrors.value;
const errorFields = Object.keys(errors).filter(key => errors[key]);
return {
isValid: errorFields.length === 0,
errorCount: errorFields.length,
errorFields,
firstError: errorFields.length > 0 ? errors[errorFields[0]] : null
};
});Checks if field value differs from initial value.
/**
* Checks if field value differs from initial value
* @param path - Field path (uses injected field context if omitted)
* @returns Computed ref to field dirty state
*/
function useIsFieldDirty(path?: MaybeRefOrGetter<string>): ComputedRef<boolean>;Checks if field has been interacted with.
/**
* Checks if field has been interacted with
* @param path - Field path (uses injected field context if omitted)
* @returns Computed ref to field touched state
*/
function useIsFieldTouched(path?: MaybeRefOrGetter<string>): ComputedRef<boolean>;Checks if field passes validation.
/**
* Checks if field passes validation
* @param path - Field path (uses injected field context if omitted)
* @returns Computed ref to field valid state
*/
function useIsFieldValid(path?: MaybeRefOrGetter<string>): ComputedRef<boolean>;Field Validation State Examples:
import {
useIsFieldDirty,
useIsFieldTouched,
useIsFieldValid,
useField
} from "vee-validate";
// Field state composables with explicit paths
const isEmailDirty = useIsFieldDirty('email');
const isEmailTouched = useIsFieldTouched('email');
const isEmailValid = useIsFieldValid('email');
// Combined field state checks
const emailFieldStatus = computed(() => {
if (!isEmailTouched.value) return 'untouched';
if (!isEmailValid.value) return 'invalid';
if (isEmailDirty.value) return 'modified';
return 'valid';
});
// Using within field context
const EmailField = {
setup() {
useField('email', 'required|email');
const isDirty = useIsFieldDirty();
const isTouched = useIsFieldTouched();
const isValid = useIsFieldValid();
const showValidationIcon = computed(() => {
return isTouched.value && (isValid.value || !isValid.value);
});
const fieldClasses = computed(() => ({
'field-untouched': !isTouched.value,
'field-dirty': isDirty.value,
'field-valid': isTouched.value && isValid.value,
'field-invalid': isTouched.value && !isValid.value
}));
return {
isDirty,
isTouched,
isValid,
showValidationIcon,
fieldClasses
};
}
};
// Reactive field paths
const activeField = ref('username');
const isActiveFieldDirty = useIsFieldDirty(activeField);
const isActiveFieldValid = useIsFieldValid(activeField);
// Watch state changes
watch([isActiveFieldDirty, isActiveFieldValid], ([dirty, valid]) => {
console.log(`${activeField.value} state:`, { dirty, valid });
});Checks if any form field is dirty.
/**
* Checks if any form field is dirty
* @returns Computed ref to form dirty state
*/
function useIsFormDirty(): ComputedRef<boolean>;Checks if any form field is touched.
/**
* Checks if any form field is touched
* @returns Computed ref to form touched state
*/
function useIsFormTouched(): ComputedRef<boolean>;Checks if entire form is valid.
/**
* Checks if entire form is valid
* @returns Computed ref to form valid state
*/
function useIsFormValid(): ComputedRef<boolean>;Checks if form is currently submitting.
/**
* Checks if form is currently submitting
* @returns Computed ref to form submitting state
*/
function useIsSubmitting(): ComputedRef<boolean>;Checks if form is currently validating.
/**
* Checks if form is currently validating
* @returns Computed ref to form validating state
*/
function useIsValidating(): ComputedRef<boolean>;Form Validation State Examples:
import {
useIsFormDirty,
useIsFormTouched,
useIsFormValid,
useIsSubmitting,
useIsValidating,
useForm
} from "vee-validate";
// Setup form
const { handleSubmit } = useForm();
// Form state composables
const isFormDirty = useIsFormDirty();
const isFormTouched = useIsFormTouched();
const isFormValid = useIsFormValid();
const isSubmitting = useIsSubmitting();
const isValidating = useIsValidating();
// Combined form state
const formState = computed(() => ({
dirty: isFormDirty.value,
touched: isFormTouched.value,
valid: isFormValid.value,
submitting: isSubmitting.value,
validating: isValidating.value
}));
// Form status computed properties
const canSubmit = computed(() => {
return isFormValid.value && !isSubmitting.value && !isValidating.value;
});
const showUnsavedWarning = computed(() => {
return isFormDirty.value && !isSubmitting.value;
});
const formStatus = computed(() => {
if (isSubmitting.value) return 'submitting';
if (isValidating.value) return 'validating';
if (!isFormTouched.value) return 'pristine';
if (!isFormValid.value) return 'invalid';
if (isFormDirty.value) return 'modified';
return 'valid';
});
// Button states based on form state
const submitButtonProps = computed(() => ({
disabled: !canSubmit.value,
loading: isSubmitting.value,
text: isSubmitting.value ? 'Submitting...' : 'Submit'
}));
const resetButtonProps = computed(() => ({
disabled: !isFormDirty.value || isSubmitting.value,
visible: isFormDirty.value
}));
// Form progress indicator
const formProgress = computed(() => {
const touched = isFormTouched.value;
const valid = isFormValid.value;
const dirty = isFormDirty.value;
if (!touched) return { step: 0, label: 'Start filling the form' };
if (!valid) return { step: 1, label: 'Fix validation errors' };
if (dirty) return { step: 2, label: 'Ready to submit' };
return { step: 3, label: 'Form completed' };
});
// Watch form state changes
watchEffect(() => {
if (showUnsavedWarning.value) {
// Show browser warning about unsaved changes
window.addEventListener('beforeunload', handleBeforeUnload);
} else {
window.removeEventListener('beforeunload', handleBeforeUnload);
}
});
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
e.preventDefault();
e.returnValue = 'You have unsaved changes. Are you sure you want to leave?';
return e.returnValue;
};Creating custom computed properties based on multiple state sources.
import {
useFormValues,
useFormErrors,
useIsFormValid,
useIsFormDirty
} from "vee-validate";
// Custom selectors
const formValues = useFormValues();
const formErrors = useFormErrors();
const isFormValid = useIsFormValid();
const isFormDirty = useIsFormDirty();
// Complex state selectors
const formCompleteness = computed(() => {
const values = formValues.value;
const requiredFields = ['name', 'email', 'phone'];
const filledRequired = requiredFields.filter(field => values[field]).length;
return {
percentage: (filledRequired / requiredFields.length) * 100,
filledFields: filledRequired,
totalRequired: requiredFields.length,
isComplete: filledRequired === requiredFields.length
};
});
const validationSummary = computed(() => {
const errors = formErrors.value;
const errorEntries = Object.entries(errors).filter(([_, error]) => error);
return {
hasErrors: errorEntries.length > 0,
errorCount: errorEntries.length,
fields: errorEntries.map(([field, error]) => ({ field, error })),
severity: errorEntries.length > 3 ? 'high' : errorEntries.length > 0 ? 'medium' : 'none'
};
});
const formHealthScore = computed(() => {
const valid = isFormValid.value;
const dirty = isFormDirty.value;
const completeness = formCompleteness.value.percentage;
const errors = validationSummary.value.errorCount;
let score = 0;
if (valid) score += 40;
if (completeness > 80) score += 30;
if (dirty && valid) score += 20;
if (errors === 0) score += 10;
return {
score,
grade: score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : 'D',
status: score >= 90 ? 'excellent' : score >= 80 ? 'good' : score >= 70 ? 'fair' : 'poor'
};
});Install with Tessl CLI
npx tessl i tessl/npm-vee-validate