Validation System
Enforma provides a flexible validation system supporting multiple validation libraries, ensuring data integrity while maintaining a smooth user experience.
Enforma's validation approach focuses on:
- Immediate feedback - Issues are detected and reported as users interact with the form
- Clear error messages - Helpful, specific messages guide users to correct issues
- Localization - Support for internationalized validation messages
- Cross-field validation - Fields can be validated in relation to one another
- Multiple validators - Choose from Zod, Yup, Valibot, @encolajs/validator, or use no validation
- Tree-shakable - Only bundle the validator you actually use
Supported Validators
Starting with v1.3.0, Enforma supports multiple validation libraries. Choose the one that best fits your project needs:
- @encolajs/validator - Laravel-style validation rules with pipe notation (
required|email|min:8) - Zod - TypeScript-first schema validation with excellent type inference
- Yup - Object schema validation with powerful conditional logic
- Valibot - Lightweight, modular validation library for optimal bundle size
- No validation - Use forms without any validation for simple data collection
Each validator integrates seamlessly with Enforma's useForm() composable and all form components.
Integration with @encolajs/validator
The EncolaJS Validator provides Laravel-style validation rules with a simple pipe notation, making it easy to express complex validation requirements.
Installation
npm install @encolajs/validatorBasic Usage
Import the validator factory and create a validator instance:
<script setup>
import { useForm } from '@encolajs/enforma'
import { createEncolaValidator } from '@encolajs/enforma/validators/encola'
const formData = { email: '', name: '' }
const validator = createEncolaValidator({
email: 'required|email',
name: 'required|min_length:2'
})
const form = useForm(formData, validator)
</script>Form-Level Validation
Enforma supports validation at multiple levels:
- Form-level validation - Validator passed to the form component
- Field-level validation - Rules specified on individual fields
- Schema-level validation - Rules defined in form schemas
Using with form components:
<template>
<Enforma v-bind="formProps">
... form content goes here ...
</Enforma>
</template>
<script setup>
import { createEncolaValidator } from '@encolajs/enforma/validators/encola'
const formData = {
email: '',
password: ''
}
const validator = createEncolaValidator(
{
email: 'required|email',
password: 'required|min:8|same_as:@password_confirmation',
password_confirmation: 'required'
},
{
'email.required': 'Please provide your email address',
'password.min': 'Password must be at least 8 characters',
'password.same_as': 'Passwords must match',
}
)
const formProps = {
data: formData,
validator
}
</script>Field-Level Validation
You can specify validation rules directly on individual <EnformaField> components:
<template>
<Enforma :data="formData" :submitHandler="submit">
<EnformaField
name="email"
label="Email"
inputComponent="input"
rules="required|email"
:messages="{
required: 'Email is required',
email: 'Please enter a valid email address'
}"
/>
<EnformaField
name="phone"
label="Phone"
inputComponent="input"
rules="required|phone"
:messages="{
required: 'Phone number is required',
phone: 'Please enter a valid phone number'
}"
/>
</Enforma>
</template>Custom Messages
Customize error messages by passing a second parameter to createEncolaValidator:
<script setup>
import { createEncolaValidator } from '@encolajs/enforma/validators/encola'
const validator = createEncolaValidator(
{
email: 'required|email',
password: 'required|min_length:8'
},
{
'email.required': 'Please enter your email address',
'email.email': 'Please enter a valid email',
'password.min_length': 'Password must be at least 8 characters'
}
)
</script>Using with HeadlessForm
<template>
<HeadlessForm :data="formData" :validator="validator">
<HeadlessField name="email" #default="{ field, error }">
<input v-bind="field" />
<span v-if="error">{{ error }}</span>
</HeadlessField>
</HeadlessForm>
</template>
<script setup>
import { ref } from 'vue'
import { HeadlessForm, HeadlessField } from '@encolajs/enforma'
import { createEncolaValidator } from '@encolajs/enforma/validators/encola'
const formData = ref({ email: '' })
const validator = createEncolaValidator({ email: 'required|email' })
</script>Built-in Validation Rules
EncolaJS Validator provides a comprehensive set of validation rules. Common rules include:
required- Field must not be emptyemail- Must be a valid email addressmin:{value}- Minimum string length or numeric valuemax:{value}- Maximum string length or numeric valuegt:{value}- Greater than (exclusive)gte:{value}- Greater than or equal (inclusive)lt:{value}- Less than (exclusive)lte:{value}- Less than or equal (inclusive)date:{format}- Must be a valid datenumber- Must be a numberphone- Must be a valid phone numberurl- Must be a valid URLsame_as:@field- Must match another field- ... and many more
For a complete list of available validation rules, visit the EncolaJS Validator Documentation.
Cross-field Validation
EncolaJS Validator supports validators that reference other field values using the @ prefix:
const validator = createEncolaValidator({
password: 'required|min:8|same_as:@password_confirmation',
password_confirmation: 'required'
})This ensures the password and password_confirmation fields match.
Async Validation
The EncolaJS Validator library is async by default, so any function that returns a boolean-resolving Promise can be used as a validation rule.
For implementing a server-calling validation rule, check out the "Async Validation" recipe.
Migrating from Legacy API
DEPRECATED API
The legacy :rules prop and rules object parameter are deprecated but still supported in v1.3.0.
See the Migration Guide for complete migration instructions.
Integration with Zod
Zod is a TypeScript-first schema validation library with excellent type inference and error messages.
Installation
npm install zodBasic Usage
<script setup lang="ts">
import { useForm } from '@encolajs/enforma'
import { createZodValidator } from '@encolajs/enforma/validators/zod'
import { z } from 'zod'
const schema = z.object({
email: z.string().email('Invalid email address'),
name: z.string().min(2, 'Name must be at least 2 characters'),
age: z.number().min(18, 'Must be 18 or older')
})
type FormData = z.infer<typeof schema>
const formData: FormData = { email: '', name: '', age: 0 }
const form = useForm(formData, createZodValidator(schema))
</script>Complex Validation
Zod supports complex validation scenarios with chained validators:
<script setup lang="ts">
import { z } from 'zod'
import { createZodValidator } from '@encolajs/enforma/validators/zod'
const schema = z.object({
password: z.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[A-Z]/, 'Must contain uppercase letter')
.regex(/[0-9]/, 'Must contain a number'),
email: z.string().email('Invalid email'),
age: z.number().min(18).max(120)
})
const validator = createZodValidator(schema)
</script>Nested Objects
Validate complex nested data structures:
<script setup lang="ts">
import { z } from 'zod'
import { createZodValidator } from '@encolajs/enforma/validators/zod'
const schema = z.object({
user: z.object({
name: z.string().min(2),
email: z.string().email()
}),
address: z.object({
street: z.string().min(1),
city: z.string().min(1),
zipCode: z.string().regex(/^\d{5}$/, 'Must be 5 digits')
})
})
const validator = createZodValidator(schema)
</script>Using with HeadlessForm
<template>
<HeadlessForm :data="formData" :validator="validator">
<HeadlessField name="email" #default="{ field, error }">
<input type="email" v-bind="field" />
<span v-if="error" class="error">{{ error }}</span>
</HeadlessField>
<button type="submit">Submit</button>
</HeadlessForm>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { HeadlessForm, HeadlessField } from '@encolajs/enforma'
import { createZodValidator } from '@encolajs/enforma/validators/zod'
import { z } from 'zod'
const schema = z.object({
email: z.string().email('Please enter a valid email')
})
const formData = ref({ email: '' })
const validator = createZodValidator(schema)
</script>Learn more in the Zod documentation.
Integration with Yup
Yup is a powerful schema validation library with excellent support for conditional validation and cross-field dependencies.
Installation
npm install yupBasic Usage
<script setup>
import { useForm } from '@encolajs/enforma'
import { createYupValidator } from '@encolajs/enforma/validators/yup'
import * as yup from 'yup'
const schema = yup.object({
email: yup.string().email('Invalid email').required('Email is required'),
password: yup.string()
.min(8, 'Password must be at least 8 characters')
.required('Password is required'),
confirmPassword: yup.string()
.oneOf([yup.ref('password')], 'Passwords must match')
.required('Confirm password is required')
})
const formData = { email: '', password: '', confirmPassword: '' }
const form = useForm(formData, createYupValidator(schema))
</script>Conditional Validation
Yup excels at conditional validation based on other field values:
<script setup>
import * as yup from 'yup'
import { createYupValidator } from '@encolajs/enforma/validators/yup'
const schema = yup.object({
country: yup.string().required('Country is required'),
zipCode: yup.string()
.when('country', {
is: 'US',
then: (schema) => schema.matches(/^\d{5}$/, 'US zip must be 5 digits'),
otherwise: (schema) => schema.required('Zip code is required')
})
})
const validator = createYupValidator(schema)
</script>Cross-field Validation
Validate fields in relation to each other:
<script setup>
import * as yup from 'yup'
const schema = yup.object({
startDate: yup.date().required('Start date is required'),
endDate: yup.date()
.min(yup.ref('startDate'), 'End date must be after start date')
.required('End date is required')
})
</script>Nested Objects and Arrays
<script setup>
import * as yup from 'yup'
const schema = yup.object({
user: yup.object({
name: yup.string().min(2).required(),
email: yup.string().email().required()
}),
tags: yup.array()
.of(yup.string().min(1).required())
.min(1, 'At least one tag is required')
})
</script>Using with HeadlessForm
<template>
<HeadlessForm :data="formData" :validator="validator">
<HeadlessField name="email" #default="{ field, error }">
<input v-bind="field" />
<span v-if="error">{{ error }}</span>
</HeadlessField>
<HeadlessField name="password" #default="{ field, error }">
<input type="password" v-bind="field" />
<span v-if="error">{{ error }}</span>
</HeadlessField>
<HeadlessField name="confirmPassword" #default="{ field, error }">
<input type="password" v-bind="field" />
<span v-if="error">{{ error }}</span>
</HeadlessField>
</HeadlessForm>
</template>
<script setup>
import { ref } from 'vue'
import { createYupValidator } from '@encolajs/enforma/validators/yup'
import * as yup from 'yup'
const schema = yup.object({
email: yup.string().email().required(),
password: yup.string().min(8).required(),
confirmPassword: yup.string()
.oneOf([yup.ref('password')], 'Passwords must match')
.required()
})
const formData = ref({ email: '', password: '', confirmPassword: '' })
const validator = createYupValidator(schema)
</script>Learn more in the Yup documentation.
Integration with Valibot
Valibot is a lightweight, modular validation library designed for optimal bundle size. It provides a modern API with excellent TypeScript support.
Installation
npm install valibotBasic Usage
<script setup>
import { useForm } from '@encolajs/enforma'
import { createValibotValidator } from '@encolajs/enforma/validators/valibot'
import * as v from 'valibot'
const schema = v.object({
email: v.pipe(
v.string(),
v.email('Please enter a valid email address')
),
name: v.pipe(
v.string(),
v.minLength(2, 'Name must be at least 2 characters')
),
age: v.pipe(
v.number(),
v.minValue(18, 'You must be 18 or older')
)
})
const formData = { email: '', name: '', age: 0 }
const form = useForm(formData, createValibotValidator(schema))
</script>Complex Types
Valibot's modular design makes it easy to compose complex validation schemas:
<script setup>
import * as v from 'valibot'
import { createValibotValidator } from '@encolajs/enforma/validators/valibot'
const schema = v.object({
email: v.pipe(v.string(), v.email('Invalid email')),
tags: v.array(v.pipe(v.string(), v.minLength(1))),
settings: v.object({
notifications: v.boolean(),
theme: v.picklist(['light', 'dark']),
fontSize: v.pipe(v.number(), v.minValue(10), v.maxValue(24))
})
})
const validator = createValibotValidator(schema)
</script>Nested Objects
<script setup>
import * as v from 'valibot'
const schema = v.object({
user: v.object({
name: v.pipe(v.string(), v.minLength(2)),
email: v.pipe(v.string(), v.email())
}),
address: v.object({
street: v.string(),
city: v.string(),
zipCode: v.pipe(
v.string(),
v.regex(/^\d{5}$/, 'Must be 5 digits')
)
})
})
</script>Using with HeadlessForm
<template>
<HeadlessForm :data="formData" :validator="validator">
<HeadlessField name="email" #default="{ field, error }">
<input v-bind="field" />
<span v-if="error">{{ error }}</span>
</HeadlessField>
<HeadlessField name="name" #default="{ field, error }">
<input v-bind="field" />
<span v-if="error">{{ error }}</span>
</HeadlessField>
</HeadlessForm>
</template>
<script setup>
import { ref } from 'vue'
import { createValibotValidator } from '@encolajs/enforma/validators/valibot'
import * as v from 'valibot'
const schema = v.object({
email: v.pipe(v.string(), v.email('Invalid email')),
name: v.pipe(v.string(), v.minLength(2, 'Name too short'))
})
const formData = ref({ email: '', name: '' })
const validator = createValibotValidator(schema)
</script>Learn more in the Valibot documentation.
Using Enforma Without Validation
You can use Enforma forms without any validation for simple data collection scenarios.
When to Skip Validation
No validation is useful for:
- Simple data collection forms - Forms that only collect information without strict requirements
- Multi-step wizards - When validation happens only at the final step
- Server-side validation only - When all validation is handled on the backend
- Draft or autosave features - When users can save incomplete data
Basic Usage
Simply omit the validator parameter when creating a form:
<script setup>
import { useForm } from '@encolajs/enforma'
const formData = { email: '', name: '', notes: '' }
const form = useForm(formData) // No validator = no validation
// All validation methods will pass
const isValid = await form.validate() // Always returns true
</script>Using with HeadlessForm
<template>
<HeadlessForm :data="formData">
<HeadlessField name="email" #default="{ field }">
<input v-bind="field" placeholder="Email (optional)" />
</HeadlessField>
<HeadlessField name="notes" #default="{ field }">
<textarea v-bind="field" placeholder="Your notes..." />
</HeadlessField>
<button type="submit">Save Draft</button>
</HeadlessForm>
</template>
<script setup>
import { ref } from 'vue'
import { HeadlessForm, HeadlessField } from '@encolajs/enforma'
const formData = ref({
email: '',
notes: ''
})
</script>Behavior Without Validation
When no validator is provided:
form.validate()always returnstrueform.errors()always returns an empty object{}- Field error states remain empty
- Form submission is never blocked by validation
- All form data is considered valid
This allows you to use Enforma's reactive form management and data binding features without enforcing validation rules.
