Learn Vue - Forms & Validation
Series/Learn Vue/Episode 11
Episode 11 of 24

Learn Vue - Forms & Validation

This episode covers form management in Vue: two-way binding with v-model across different input types, validation patterns with VeeValidate and Vuelidate, dynamic forms with custom validation rules, plus form accessibility and user feedback.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Forms are the bridge between an application and its users: the place where you collect data, validate input, and show errors when data doesn't fit. A bad form frustrates users and lets dirty data into the backend.

Episode 11 covers form management in Vue thoroughly: input binding with v-model, validation patterns with VeeValidate and its alternative Vuelidate, dynamic forms with custom validation rules, plus accessibility and feedback so forms are comfortable for everyone.

Form Handling with v-model

Different Input Types

v-model works with almost every form element: text, checkbox, radio, and select. Vue handles the differing values and events behind the scenes:

JSBerbagai jenis input
<script setup>
import { ref } from "vue";
const nama = ref("");
const aktif = ref(false);
const paket = ref("pro");
</script>
 
<template>
  <input v-model="nama" />
  <input type="checkbox" v-model="aktif" />
  <select v-model="paket">
    <option value="gratis">Gratis</option>
    <option value="pro">Pro</option>
  </select>
</template>

v-model="aktif" on a checkbox produces a boolean, while v-model="paket" on a select binds the value of the chosen option. One directive, many input types handled automatically.

For large forms, collect state in a single reactive object and handle submission with @submit.prevent so the page doesn't reload.

Validation Patterns with VeeValidate

Install and Setup

VeeValidate is the most popular validation library in the Vue ecosystem. Install it together with the Yup schema validator:

Install VeeValidate dan Yup
npm install vee-validate yup

useField and useForm

VeeValidate uses the useField and useForm composables, which integrate with the Composition API:

JSValidasi dengan VeeValidate
<script setup>
import { useField, useForm } from "vee-validate";
import * as yup from "yup";
 
const { handleSubmit } = useForm({
  validationSchema: yup.object({
    email: yup.string().email("Email tidak valid").required("Email wajib"),
    password: yup.string().min(6, "Minimal 6 karakter").required(),
  }),
});
 
const { value: email, errorMessage: emailError } = useField("email");
const { value: password, errorMessage: passError } = useField("password");
 
const onKirim = handleSubmit((nilai) => {
  console.log("valid:", nilai);
});
</script>
 
<template>
  <form @submit="onKirim">
    <input v-model="email" />
    <span>{{ emailError }}</span>
    <input v-model="password" type="password" />
    <span>{{ passError }}</span>
    <button>Kirim</button>
  </form>
</template>

validationSchema is defined once with Yup, then useField("email") connects the field to that schema. errorMessage holds the ready-to-display error message.

Custom Validation Rules

Built-in rules aren't always enough. Create your own rule with a validator function:

JSCustom rule
import * as yup from "yup";
 
const schema = yup.object({
  username: yup
    .string()
    .test("unik", "Username sudah dipakai", async (nilai) => {
      const res = await fetch(`/api/cek-username?nama=${nilai}`);
      const { tersedia } = await res.json();
      return tersedia;
    }),
});

yup.string().test(...) accepts a rule name, an error message, and a validation function. Because it can be async, server-side checks like username lookups run smoothly.

Dynamic Forms and Vuelidate

Dynamic Fields with useFieldArray

Some forms let users add rows, for example an order items list:

JSDynamic form
<script setup>
import { useFieldArray } from "vee-validate";
 
const { fields, push, remove } = useFieldArray("items");
</script>
 
<template>
  <div v-for="(field, index) in fields" :key="field.key">
    <input v-model="field.value.nama" />
    <input v-model="field.value.qty" />
    <button @click="remove(index)">Hapus</button>
  </div>
  <button @click="push({ nama: "", qty: 1 })">Tambah baris</button>
</template>

useFieldArray("items") manages an array of fields with push to add and remove to delete. Each row gets a unique key so Vue renders correctly.

Vuelidate as an Alternative

Vuelidate attaches rules directly to reactive state and can be installed with npm install @vuelidate/core @vuelidate/validators. Errors are accessed through $errors, and $error flags a field that failed validation.

Accessibility and User Feedback

ARIA Attributes and Focus

An accessible form tells screen readers about errors and guides focus:

JSForm aksesibel
<template>
  <label for="email">Email</label>
  <input
    id="email"
    type="email"
    aria-describedby="email-hint"
    :aria-invalid="!!emailError"
  />
  <p id="email-hint">{{ emailError }}</p>
</template>

aria-describedby="email-hint" connects the input to the helper message, and :aria-invalid="!!emailError" flags the field that failed validation.

Clear Feedback

Rules of thumb for form feedback: show errors near the field in question, use human-readable text, don't rely on red color alone as the only indicator, and validate when a field loses focus rather than on every keystroke.

Summary

Episode 11 made you manage forms with confidence: v-model for every input type, VeeValidate with Yup for schema-based validation, Vuelidate as a lightweight alternative, dynamic forms with useFieldArray, plus accessibility and feedback that guides users.

Key takeaways:

  • v-model handles all form input types.
  • VeeValidate plus Yup uses a single schema for validation.
  • A custom rule with .test() can check against the server.
  • Vuelidate fits lightweight declarative validation.
  • useFieldArray for dynamic forms.
  • Accessible errors: near the field, clear text, indicators beyond color.

In the next episode 12, we'll cover authentication and authorization — client-side authentication patterns in Vue, token storage and refresh flows, route guards for protected pages, and role-based access control that restricts the UI according to a user's permissions.

Learn Vue - Forms & Validation | Learn Vue