Vue · Forms

Vue 3 Forms and Validation

Forms are where most real Vue apps spend their complexity. Getting binding, validation timing, and error display right is what separates a usable form from a frustrating one.

John Kihiu12 min read

Forms are deceptively hard. The binding is easy in Vue, but validation — when to run it, what to show, how to keep it from nagging — is where forms become frustrating or pleasant. Vue 3 gives you the primitives to build good forms by hand, and an ecosystem of libraries for when the complexity grows. Knowing the patterns matters more than the tool.

Binding with v-model

v-model gives you two-way binding between form inputs and reactive state with minimal code, and it works on custom components too, so you can build reusable input components that participate in v-model. This part is genuinely simple; the state behind the form is just refs, and the inputs stay in sync automatically. The complexity is never the binding — it is everything around validation.

Validate at the right time

Validation timing is what makes a form feel considerate or annoying. Validating every keystroke from the start flags errors before the user has finished typing, which is nagging. A good default: validate a field on blur (when the user leaves it) and, once it has shown an error, re-validate on input so they see the error clear as they fix it. Always validate everything on submit as the backstop. Getting this rhythm right is most of what makes a form pleasant.

JavaScript · a small validation pattern
const email = ref('')
const touched = ref(false)
const error = computed(() => {
  if (!touched.value) return ''
  if (!email.value) return 'Email is required'
  if (!/^[^@]+@[^@]+\.[^@]+$/.test(email.value)) return 'Enter a valid email'
  return ''
})
// @blur="touched = true"  -> error appears only after the field is left

Show clear errors

Reach for a library when it earns its place

Hand-rolling is fine for simple forms, but complex ones — many fields, cross-field rules, dynamic sections, schema validation — are where a library like VeeValidate or a schema tool (Zod, Yup) pays off, handling the tedious state and letting you declare rules. Start hand-rolled, adopt a library when the validation logic outgrows a few computed properties.

Vue 3 forms are easy to bind with v-model and hard to validate well — the craft is in timing (blur then input, always on submit), clear per-field errors, and accessible failure handling. Hand-roll the simple ones with refs and computed validators, and adopt a form library once cross-field rules and dynamic fields make the manual approach unwieldy. The library is a convenience; the patterns are what make the form good.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.