Form Validation
Validate form input reactively, showing errors as the user types or submits.
Manual Validation with Computed Properties
For a small form, a computed property per field, deriving directly from v-model state, is often all you need.
<script setup>import { ref, computed } from 'vue';
const email = ref('');
const emailError = computed(() => { if (!email.value) return 'Email is required.'; if (!email.value.includes('@')) return 'Enter a valid email.'; return '';});</script>
<template> <input v-model="email" type="email" /> <p v-if="emailError" class="error">{{ emailError }}</p></template>Validating on Submit
For a multi-field form, check all validations together at submit time, and stop the submission if anything fails.
<script setup>import { ref, computed } from 'vue';
const email = ref('');const password = ref('');
const errors = computed(() => ({ email: !email.value.includes('@') ? 'Enter a valid email.' : '', password: password.value.length < 8 ? 'At least 8 characters.' : '',}));
const isValid = computed(() => !errors.value.email && !errors.value.password);
function handleSubmit() { if (!isValid.value) return; // proceed with submission}</script>
<template> <form @submit.prevent="handleSubmit"> <input v-model="email" type="email" /> <p v-if="errors.email">{{ errors.email }}</p>
<input v-model="password" type="password" /> <p v-if="errors.password">{{ errors.password }}</p>
<button type="submit" :disabled="!isValid">Sign Up</button> </form></template>Showing Errors Only After Interaction
Showing an error the instant the page loads (before the user has typed anything) feels aggressive. Track whether a field has been "touched," and only display its error afterward.
<script setup>import { ref } from 'vue';
const email = ref('');const emailTouched = ref(false);</script>
<template> <input v-model="email" @blur="emailTouched = true" /> <p v-if="emailTouched && !email.includes('@')">Enter a valid email.</p></template>Using a Validation Library
For larger forms, VeeValidate (often paired with a schema library like Yup or Zod) handles field-level validation state, touched tracking, and error messages consistently, without hand-rolling it for every form.
A login form with two fields is fine hand-rolled. A multi-step signup or checkout form with a dozen interdependent fields is where a dedicated library like VeeValidate genuinely pays for itself.
FAQs
Yes, always — client-side validation is for UX; never trust it as your only line of defense, since it can be bypassed entirely.
Not directly — computed properties must be synchronous. For async checks, use a watcher that sets a ref based on the result of an API call.
Summary
Computed properties handle most form validation needs cleanly for small-to-medium forms, with dedicated libraries available once complexity grows. Next, you'll handle errors that occur anywhere in a component tree.