@formkit/vue and vuelidate are both libraries designed to handle form validation in Vue.js applications, but they take very different approaches. @formkit/vue is part of the FormKit ecosystem, which provides a complete form-building solution that includes UI components, validation, accessibility, and state management out of the box. vuelidate, on the other hand, is a lightweight, model-based validation library that focuses solely on validating data against defined rules without providing any UI components.
Both @formkit/vue and vuelidate help Vue developers validate user input, but they solve different problems. @formkit/vue is a full-featured form authoring framework, while vuelidate is a focused validation utility. Let’s compare how they work in practice.
@formkit/vue provides everything needed to build forms — from input components to validation logic to accessibility features. You define your form schema declaratively, and FormKit handles rendering, state management, and validation.
<!-- @formkit/vue: Complete form with built-in UI -->
<script setup>
import { FormKit } from '@formkit/vue'
const formData = {}
function handleSubmit(data) {
console.log(data)
}
</script>
<template>
<FormKit
type="form"
:actions="false"
@submit="handleSubmit"
>
<FormKit
name="email"
type="email"
label="Email address"
validation="required|email"
/>
<FormKit
name="password"
type="password"
label="Password"
validation="required|length:8"
/>
</FormKit>
</template>
vuelidate only validates data — it doesn’t render anything. You pair it with your own form markup and manage DOM interactions yourself.
<!-- vuelidate: Validation logic only -->
<script setup>
import useVuelidate from '@vuelidate/core'
import { required, email, minLength } from '@vuelidate/validators'
import { reactive } from 'vue'
const state = reactive({
email: '',
password: ''
})
const rules = {
email: { required, email },
password: { required, minLength: minLength(8) }
}
const v$ = useVuelidate(rules, state)
function handleSubmit() {
v$.value.$validate()
if (!v$.value.$error) {
console.log(state)
}
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input v-model="state.email" placeholder="Email" />
<div v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</div>
<input v-model="state.password" type="password" placeholder="Password" />
<div v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</div>
<button type="submit">Submit</button>
</form>
</template>
@formkit/vue uses its own component system. While you can create custom inputs using FormKit’s plugin architecture, you’re generally working within FormKit’s rendering pipeline.
// @formkit/vue: Creating a custom input
import { createInput } from '@formkit/vue'
const CustomRating = createInput({
// component definition
})
// Then register globally or locally
vuelidate works with any input component because it only cares about the data model. This makes it easy to plug into existing design systems.
<!-- vuelidate with Vuetify -->
<template>
<v-text-field
v-model="state.email"
:error-messages="v$.email.$errors.map(e => e.$message)"
label="Email"
/>
</template>
@formkit/vue uses string-based validation rules that are applied directly on FormKit nodes.
<FormKit
name="age"
validation="required|number|between:18,99"
/>
You can also write custom validation functions:
const customValidator = (node) => {
return node.value.length > 5 ? true : 'Must be longer than 5 characters'
}
vuelidate defines validation rules as JavaScript objects using validator functions from @vuelidate/validators or custom functions.
const rules = {
username: {
required,
unique: async (value) => {
const exists = await checkUsernameExists(value)
return !exists
}
}
}
For cross-field validation (e.g., “password and confirm password must match”), both libraries support it but with different patterns.
@formkit/vue uses the $get helper to reference other fields in validation:
<FormKit
name="confirmPassword"
validation="required|confirm:@password"
/>
Or with a custom function:
const confirmPassword = (node) => {
const password = node.parent?.$get('password')?.value
return node.value === password || 'Passwords do not match'
}
vuelidate handles cross-field validation by defining validators at the root level or using sibling references:
const rules = {
password: { required, minLength: minLength(8) },
confirmPassword: {
required,
sameAsPassword: sameAs(state.password)
}
}
@formkit/vue automatically manages ARIA attributes, focus management, error announcements for screen readers, and live regions. Input states (like loading indicators during async validation) are built in.
vuelidate leaves accessibility entirely up to you. You must manually wire up ARIA attributes, manage focus traps, and ensure error messages are announced.
Both support asynchronous validation, but the implementation differs.
@formkit/vue allows async validators that return promises:
const checkEmail = async (node) => {
const available = await api.checkEmail(node.value)
return available || 'Email already taken'
}
vuelidate supports async validators through the same object syntax:
const rules = {
email: {
async isUnique(value) {
if (!value) return true
const exists = await api.emailExists(value)
return !exists
}
}
}
@formkit/vue has a larger footprint because it includes UI components, theming, and state management. However, it reduces the amount of boilerplate you write for common form patterns.
vuelidate is much smaller since it only handles validation logic. But you’ll write more code to connect validation state to your UI and handle edge cases like debouncing or submission flow.
@formkit/vue when:vuelidate when:Think of @formkit/vue as a complete workshop for building forms — it gives you tools, materials, and blueprints. vuelidate is like a high-precision measuring tape — it does one job extremely well, but you bring everything else. Choose based on whether you need a full form solution or just validation logic.
Choose @formkit/vue if you want an all-in-one form solution that handles UI rendering, validation, accessibility, and complex form state (like conditional fields or multi-step forms) with minimal configuration. It's ideal when you prioritize developer velocity and consistent UX across forms, and don't mind adopting a more opinionated framework for form building.
Choose vuelidate if you already have your form UI built with custom components or another library (like Vuetify or Element Plus) and only need a flexible, composable validation layer. It's well-suited for projects where you want fine-grained control over validation logic without being tied to a specific component system.
FormKit is a form-authoring framework for Vue developers that makes building high quality production-ready forms 10x faster. It is easy-to-learn and ships with production-ready scaffolding like inputs, forms, submission and error handling, and validation rules. To learn more check out the documentation website at: formkit.com.
FormKit — which supports its whole feature set for native HTML inputs (like select, checkbox, and textarea) — is and will always be an MIT-licensed
open source project. Please consider sponsoring FormKit so we can sustainably
and continually improve it! There are a variety of sponsor tiers and benefits for each sponsor.
Key features | |
|---|---|
☝️ Comprehensive packageFormKit is an all-in-one form-authoring framework with input scaffolding (labels, help text, etc.), validation, form UI & styling, error handling, generation, a11y, i18n, and more! |
😎 Developer happinessForms are everywhere, yet surprisingly tedious to author — well, not anymore. FormKit provides a powerful and flexible API to developers that makes complex form creation a breeze. |
🎯 Validation built inRidiculously easy validation out-of-the-box to handle 95% of use-cases. Help text, validation rules, and validation messages are simple props. Need more? You can add custom validation rules too. |
⚡️ Blazing-fast PerformanceFormKit can handle the most demanding forms — wizards, multi-step, deeply nested repeating groups, and more. |
🔌 Plugin systemExtend FormKit's functionality or reuse custom inputs, validation rules and messages across projects by tapping into the plugin system. Make your plugin open source to share with others! |
✨ Generate formsGenerate an entire form from a JSON-compatible schema. Schema allows you to render complex forms from JSON with conditional rendering, logic, dynamic data, groups, wrappers, HTML elements, and custom components. |
🎨 Robust ThemingFormKit works easily alongside your favorite UI frameworks like Bootstrap and utility-class tools like Tailwind. With numerous ways to modify classes and DOM structure, integrating FormKit plays nicely with any frontend. |
🌐 InternationalizationFormKit is made for all! Thanks to the FormKit community, FormKit ships with support for many languages. Don't see your language? Contribute one with our locale builder. |
Thank you for your willingness to contribute to this free and open source project! When contributing, consider first discussing your desired change with the core team via GitHub issues, Discord, or other method.
This project exists thanks to all the people who volunteer their time to contribute!
Copyright (c) 2021-present, FormKit, Inc.