CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vee-validate

Painless forms for Vue.js with comprehensive validation, composition API, and component-based approaches.

Pending
Overview
Eval results
Files

form-management.mddocs/

Form Management

Form-level composables for managing complete form state, validation, submission handling, and providing context to child components. These composables are the foundation for building complex forms with VeeValidate.

Capabilities

useForm Composable

Creates a form context with comprehensive validation management and state tracking.

/**
 * Creates a form context with validation management
 * @param options - Optional form configuration
 * @returns Form context object with state and methods
 */
function useForm<TValues extends GenericObject = GenericObject, TOutput extends GenericObject = TValues>(
  options?: FormOptions<TValues>
): FormContext<TValues, TOutput>;

interface FormOptions<TValues extends GenericObject> {
  validationSchema?: MaybeRef<RawFormSchema<TValues> | TypedSchema<TValues, TOutput> | YupSchema<TValues> | undefined>;
  initialValues?: PartialDeep<TValues>;
  initialErrors?: Partial<FlattenAndSetPathsType<TValues, string | string[] | undefined>>;
  initialTouched?: Partial<FlattenAndSetPathsType<TValues, boolean>>;
  validateOnMount?: boolean;
  keepValuesOnUnmount?: MaybeRef<boolean>;
  name?: string;
}

interface FormContext<TValues extends GenericObject = GenericObject, TOutput extends GenericObject = TValues> {
  // Form state
  values: TValues;
  errors: ComputedRef<FormErrors<TValues>>;
  meta: ComputedRef<FormMeta<TValues>>;
  isSubmitting: Ref<boolean>;
  isValidating: Ref<boolean>;
  submitCount: Ref<number>;
  controlledValues: Ref<TValues>;
  
  // Form methods
  handleSubmit: HandleSubmitFactory<TValues, TOutput> & { withControlled: HandleSubmitFactory<TValues, TOutput> };
  validate(opts?: Partial<ValidationOptions>): Promise<FormValidationResult<TValues, TOutput>>;
  validateField<TPath extends Path<TValues>>(field: TPath, opts?: Partial<ValidationOptions>): Promise<ValidationResult<TOutput[TPath]>>;
  
  // State mutations
  setFieldValue<T extends Path<TValues>>(field: T, value: PathValue<TValues, T>, shouldValidate?: boolean): void;
  setFieldError(field: Path<TValues>, message: string | string[] | undefined): void;
  setErrors(fields: Partial<FlattenAndSetPathsType<TValues, string | string[] | undefined>>): void;
  setValues(fields: PartialDeep<TValues>, shouldValidate?: boolean): void;
  setFieldTouched(field: Path<TValues>, isTouched: boolean): void;
  setTouched(fields: Partial<Record<Path<TValues>, boolean>> | boolean): void;
  resetForm(state?: Partial<FormState<TValues>>, opts?: Partial<ResetFormOpts>): void;
  resetField(field: Path<TValues>, state?: Partial<FieldState>): void;
  
  // Form context methods
  handleReset(): void;
  submitForm(e?: unknown): Promise<void>;
  createPathState<TPath extends Path<TValues>>(path: MaybeRef<TPath>, config?: Partial<PathStateConfig<TOutput[TPath]>>): PathState<PathValue<TValues, TPath>>;
  defineField<TPath extends Path<TValues>, TValue = PathValue<TValues, TPath>, TExtras extends GenericObject = GenericObject>(
    path: MaybeRefOrGetter<TPath>,
    config?: Partial<InputBindsConfig<TValue, TExtras>> | LazyInputBindsConfig<TValue, TExtras>
  ): [Ref<TValue>, Ref<BaseFieldProps & TExtras>];
}

type HandleSubmitFactory<TValues extends GenericObject, TOutput extends GenericObject = TValues> = <TReturn = unknown>(
  cb: SubmissionHandler<TValues, TOutput, TReturn>,
  onSubmitValidationErrorCb?: InvalidSubmissionHandler<TValues, TOutput>
) => (e?: Event) => Promise<TReturn | undefined>;

Usage Examples:

import { useForm } from "vee-validate";
import * as yup from "yup";

// Basic form setup
const { handleSubmit, errors, meta, values } = useForm();

// Form with validation schema
const schema = yup.object({
  name: yup.string().required("Name is required"),
  email: yup.string().email("Email is invalid").required("Email is required"),
  age: yup.number().min(18, "Must be at least 18").required("Age is required")
});

const { 
  handleSubmit, 
  errors, 
  meta, 
  values,
  setFieldValue,
  resetForm 
} = useForm({
  validationSchema: schema,
  initialValues: {
    name: "",
    email: "",
    age: 0
  }
});

// Handle form submission
const onSubmit = handleSubmit((values) => {
  console.log("Form submitted:", values);
  // Submit to API
}, (ctx) => {
  console.log("Form validation failed:", ctx.errors);
});

// Programmatic field manipulation
const updateUser = () => {
  setFieldValue("name", "John Doe");
  setFieldValue("email", "john@example.com");
};

// Reset form to initial state
const handleReset = () => {
  resetForm();
};

// Advanced form with custom validation
const { 
  handleSubmit, 
  validate, 
  validateField,
  setErrors 
} = useForm({
  validationSchema: {
    username: (value) => {
      if (!value) return "Username is required";
      if (value.length < 3) return "Username too short";
      return true;
    },
    password: (value) => {
      if (!value) return "Password is required";
      if (value.length < 8) return "Password must be at least 8 characters";
      return true;
    },
    confirmPassword: (value, { form }) => {
      if (!value) return "Please confirm password";
      if (value !== form.password) return "Passwords do not match";
      return true;
    }
  }
});

// Manual validation
const validateForm = async () => {
  const result = await validate();
  if (!result.valid) {
    console.log("Form is invalid:", result.errors);
  }
};

// Validate specific field
const validateUsername = async () => {
  const result = await validateField("username");
  if (!result.valid) {
    console.log("Username is invalid:", result.errors);
  }
};

useFormContext Composable

Accesses the current form context via dependency injection from a parent Form component or useForm call.

/**
 * Accesses the current form context via dependency injection
 * @returns Form context from nearest parent Form component or useForm call
 * @throws Error if no form context is found
 */
function useFormContext<TValues extends GenericObject = GenericObject, TOutput extends GenericObject = TValues>(): FormContext<TValues, TOutput>;

Usage Examples:

import { useFormContext } from "vee-validate";

// In a child component that needs access to form context
const ChildComponent = {
  setup() {
    const form = useFormContext();
    
    // Access form state
    const isFormValid = computed(() => form.meta.value.valid);
    const formErrors = computed(() => form.errors.value);
    
    // Use form methods
    const clearForm = () => {
      form.resetForm();
    };
    
    const submitForm = async () => {
      const result = await form.validate();
      if (result.valid) {
        // Handle submission
      }
    };
    
    return {
      isFormValid,
      formErrors,
      clearForm,
      submitForm
    };
  }
};

Form State Types

FormMeta Interface

Provides aggregate metadata about the form's validation state.

interface FormMeta<TValues extends GenericObject> {
  touched: boolean;      // True if any field has been touched
  dirty: boolean;        // True if any field value differs from initial
  valid: boolean;        // True if all fields pass validation
  pending: boolean;      // True if any validation is in progress
  initialValues?: Partial<TValues>;  // Original form values
}

FormState Interface

Complete form state structure for reset operations and state management.

interface FormState<TValues> {
  values: PartialDeep<TValues>;                                    // Current field values
  errors: Partial<Record<Path<TValues>, string | undefined>>;      // Field error messages
  touched: Partial<Record<Path<TValues>, boolean>>;                // Field touched states
  submitCount: number;                                             // Number of submission attempts
}

FormErrors Type

Type-safe error mapping for form fields.

type FormErrors<TValues extends GenericObject> = Partial<Record<Path<TValues> | '', string | undefined>>;
type FormErrorBag<TValues extends GenericObject> = Partial<Record<Path<TValues> | '', string[]>>;

Submission Handling

SubmissionHandler Type

Type definition for form submission callbacks.

type SubmissionHandler<TInput extends GenericObject = GenericObject, TOutput = TInput, TReturn = unknown> = (
  values: TOutput,
  ctx: SubmissionContext<TInput>
) => TReturn;

interface SubmissionContext<TInput extends GenericObject = GenericObject> extends FormActions<TInput> {
  evt?: Event;                           // Original submit event
  controlledValues: Partial<TInput>;     // Current controlled form values
}

InvalidSubmissionHandler Type

Type definition for handling invalid form submissions.

type InvalidSubmissionHandler<TInput extends GenericObject = GenericObject, TOutput extends GenericObject = TInput> = (
  ctx: InvalidSubmissionContext<TInput, TOutput>
) => void;

interface InvalidSubmissionContext<TInput extends GenericObject = GenericObject, TOutput extends GenericObject = TInput> {
  values: TInput;                                                   // Current form values
  evt?: Event;                                                      // Original submit event
  errors: Partial<Record<Path<TInput>, string>>;                    // Current field errors
  results: FormValidationResult<TInput, TOutput>['results'];       // Detailed validation results
}

Submission Examples:

import { useForm } from "vee-validate";

const { handleSubmit } = useForm();

// Basic submission
const onSubmit = handleSubmit((values) => {
  console.log("Submitting:", values);
  return fetch("/api/submit", {
    method: "POST",
    body: JSON.stringify(values)
  });
});

// Submission with error handling
const onSubmitWithErrorHandling = handleSubmit(
  async (values, { setFieldError, setErrors }) => {
    try {
      const response = await fetch("/api/submit", {
        method: "POST",
        body: JSON.stringify(values)
      });
      
      if (!response.ok) {
        const errors = await response.json();
        setErrors(errors);
        return;
      }
      
      console.log("Success!");
    } catch (error) {
      setFieldError("", "Submission failed");
    }
  },
  ({ errors, results }) => {
    console.log("Form is invalid:", errors);
    console.log("Validation results:", results);
  }
);

// Controlled form submission
const onControlledSubmit = handleSubmit.withControlled((values, ctx) => {
  // Uses controlled values instead of current form values
  console.log("Controlled values:", ctx.controlledValues);
  return submitToAPI(ctx.controlledValues);
});

Advanced Form Configuration

Schema-based Forms

Using validation schemas for comprehensive form validation.

import { useForm } from "vee-validate";
import * as yup from "yup";

// Yup schema
const yupSchema = yup.object({
  user: yup.object({
    name: yup.string().required(),
    email: yup.string().email().required(),
    profile: yup.object({
      bio: yup.string().max(500),
      age: yup.number().min(18)
    })
  })
});

const { handleSubmit, errors } = useForm({
  validationSchema: yupSchema,
  initialValues: {
    user: {
      name: "",
      email: "",  
      profile: {
        bio: "",
        age: 0
      }
    }
  }
});

// Raw schema with function validators
const rawSchema = {
  "user.name": (value) => value ? true : "Name is required",
  "user.email": [
    (value) => value ? true : "Email is required",
    (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || "Email is invalid"
  ],
  "user.profile.bio": (value) => value.length <= 500 || "Bio too long"
};

const { handleSubmit: handleRawSubmit } = useForm({
  validationSchema: rawSchema
});

Install with Tessl CLI

npx tessl i tessl/npm-vee-validate

docs

configuration-rules.md

core-validation.md

field-management.md

form-actions.md

form-management.md

index.md

state-access.md

vue-components.md

tile.json