createValidation
Per-input validation with reactive rules, async validation, and Standard Schema support.
Usage
Standalone
Create a validation instance with rules. Pass a value source so validate() reads from it automatically:
import { createValidation } from '@vuetify/v0'
import { shallowRef } from 'vue'
const email = shallowRef('')
const validation = createValidation({
value: email,
rules: [
v => !!v || 'Required',
v => /^.+@\S+\.\S+$/.test(String(v)) || 'Invalid email',
],
})
await validation.validate()
console.log(validation.errors.value) // ['Required', 'Invalid email']
console.log(validation.isValid.value) // false
validation.reset()Explicit Value
Pass the value directly to validate() instead of storing a value source:
const validation = createValidation({
rules: [v => !!v || 'Required'],
})
await validation.validate('') // validate with empty string
await validation.validate('hello') // validate with 'hello'With Rule Aliases
When a rules context is provided via createRulesPlugin or createRulesContext, alias strings resolve automatically:
const validation = createValidation({
rules: ['required', 'slug'],
})With Standard Schema
Any Standard Schema↗︎-compliant library works without an adapter — pass the schema object directly and it’s auto-detected:
import { z } from 'zod'
const validation = createValidation({
rules: [z.coerce.number().int().min(18, 'Must be 18+')],
})import * as v from 'valibot'
const validation = createValidation({
rules: [v.pipe(v.string(), v.email('Invalid email'))],
})import { type } from 'arktype'
const validation = createValidation({
rules: [type('string.email')],
})Dynamic Rules
Register rules individually after creation:
const validation = createValidation()
validation.register(v => !!v || 'Required')
validation.register(v => /^.+@\S+\.\S+$/.test(String(v)) || 'Invalid email')Use onboard() to register multiple rules at once:
validation.onboard([
v => !!v || 'Required',
v => v.length >= 8 || 'Min 8 characters',
v => /[A-Z]/.test(String(v)) || 'Must contain uppercase',
])Enabling and Disabling Rules
Each rule is a ticket with selection methods from createGroup. The enroll option (default true) controls whether newly registered rules are active immediately. Set enroll: false to register rules in an inactive state:
const validation = createValidation({
rules: [
v => !!v || 'Required',
v => /^.+@\S+\.\S+$/.test(String(v)) || 'Invalid email',
],
})
// Disable the email format rule
const [, format] = [...validation.values()]
format.unselect()
await validation.validate('')
// Only 'Required' runs — format rule is inactive
// Re-enable it
format.select()Silent Validation
Check validity without updating the UI:
const valid = await validation.validate('', true) // silent = true
// validation.errors.value is unchanged
// validation.isValid.value is unchangedAuto-Registration with Forms
When created inside a component with a parent form context, createValidation auto-registers with the form. The form can then coordinate submit and reset across all registered validations. Cleanup happens automatically via onScopeDispose:
<script setup lang="ts">
import { createValidation } from '@vuetify/v0'
import { shallowRef } from 'vue'
// Parent provides form context via createFormContext or createFormPlugin
// This validation auto-registers with it
const email = shallowRef('')
const validation = createValidation({
value: email,
rules: ['required', 'email'],
})
</script>Architecture
createValidation extends createGroup with per-input validation state. Each ticket is a rule. When a parent form context exists, it auto-registers:
Race Safety
Async validation uses a generation counter to prevent stale results. If a newer validation starts before an older one completes, the older result is discarded.
Reactivity
Context-level state is fully reactive. Rule tickets inherit selection reactivity from createGroup.
| Property/Method | Reactive | Notes |
|---|---|---|
errors | ShallowRef array of error strings | |
isValid | ShallowRef (null/true/false) | |
isValidating | ShallowRef boolean | |
selectedIds | Reactive Set of active rule IDs | |
ticket.isSelected | Ref boolean per rule |
Examples
Validation State
status: idle
isValid: null
isValidating: false
touched: false
errors: 0
Validation runs on blur. Try "jane@example.com" (registered) or "you@example.com" (available).
FAQ
Yes. Any Standard Schema↗︎-compliant schema works without an adapter — pass the schema object directly in rules and it’s auto-detected alongside function rules and alias strings.
Pass silent as the second argument: validate(value, true). It returns the boolean result while leaving errors and isValid unchanged.
createValidation owns one input’s rules and validity state; createForm is a registry that coordinates submit and reset across many validations. A validation created inside a form’s tree auto-registers with it.
Each rule is a createGroup ticket — call unselect() on it to deactivate (so validate() skips it) and select() to re-enable. Register rules inactive from the start with enroll: false.
The stale result is discarded. Async validation uses a generation counter, so only the latest validate() call updates errors and isValid — a second blur while a check is pending won’t clobber the state with an outdated result.