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
Async Validation
Username availability check with async rules and generation-based race safety. Demonstrates isValidating spinner, error display, and tri-state isValid.
Validation State
isValid: null
isValidating: false
errors: 0
rules: 4 (4 active)
Try: "admin", "root", "test" (taken) or "hello" (available)
Enabling and Disabling Rules
Toggle individual validation rules on/off at runtime using the selection API inherited from createGroup.
Active Rules
4/4 rules active · Only selected rules run during validation.
Functions
createValidation
(_options?: ValidationOptions) => ValidationContext<ValidationTicketInput, ValidationTicket<ValidationTicketInput>>Creates a per-input validation instance. Built on `createGroup` — each registered rule becomes a ticket that can be enabled/disabled via selection methods (`select`, `unselect`, `toggle`). Only selected (active) rules run during validation.
Options
disabled
MaybeRefOrGetter<boolean> | undefinedDisabled state for the entire model instance
Default: false
multiple
MaybeRefOrGetter<boolean> | undefinedAllow multiple tickets to be selected simultaneously
Default: false
mandatory
MaybeRefOrGetter<boolean | "force"> | undefinedControls mandatory selection behavior: - `false` (default): No mandatory selection enforcement - `true`: Prevents deselecting the last selected item - `'force'`: Automatically selects the first non-disabled item on registration
Properties
selectedValues
ComputedRef<Set<E["value"] extends Ref<infer U, infer U> ? U : E["value"]>>Computed Set of selected ticket values
selectedIndexes
ComputedRef<Set<number>>Methods
move
(id: ID, toIndex: number) => E | undefinedSeek for a ticket based on direction and optional predicate
seek
(direction?: "first" | "last", from?: number, predicate?: (ticket) => boolean) => E | undefinedon
<K extends Extensible<RegistryEventName>>(event: K, cb: EventHandler<E, K>) => voidListen for registry events
off
<K extends Extensible<RegistryEventName>>(event: K, cb: EventHandler<E, K>) => voidStop listening for registry events
emit
<K extends Extensible<RegistryEventName>>(event: K, data: EventPayload<E, K>) => voidEmit an event with data
batch
<R>(fn: () => R) => RExecute operations in a batch, deferring cache invalidation and event emission until complete
register
(rule: RuleInput | Partial<Z>) => Eonboard
(rules: (RuleInput | Partial<Z>)[]) => E[]validate
(value?: unknown, silent?: boolean) => Promise<boolean>Run validation against all active (selected) rules.