0
# Configuration and Rules
1
2
Global configuration and custom validation rule definition for VeeValidate. These functions allow you to customize validation behavior and define reusable validation rules across your application.
3
4
## Capabilities
5
6
### defineRule Function
7
8
Registers a custom validation rule globally that can be used throughout the application.
9
10
```typescript { .api }
11
/**
12
* Registers a custom validation rule globally
13
* @param id - Unique rule identifier
14
* @param validator - Rule implementation function
15
*/
16
function defineRule<TValue = unknown, TParams extends any[] = any[]>(
17
id: string,
18
validator: ValidationRuleFunction<TValue, TParams> | SimpleValidationRuleFunction<TValue, TParams>
19
): void;
20
21
type ValidationRuleFunction<TValue, TParams extends any[]> = (
22
value: TValue,
23
params: TParams,
24
ctx: FieldValidationMetaInfo
25
) => MaybePromise<boolean | string>;
26
27
type SimpleValidationRuleFunction<TValue, TParams extends any[]> = (
28
value: TValue,
29
...params: TParams
30
) => MaybePromise<boolean | string>;
31
32
interface FieldValidationMetaInfo {
33
field: string; // Field name
34
name: string; // Field display name
35
label?: string; // Field label
36
form: Record<string, unknown>; // Current form values
37
rule?: { // Rule information
38
name: string; // Rule name
39
params?: Record<string, unknown> | unknown[]; // Rule parameters
40
};
41
}
42
```
43
44
**defineRule Examples:**
45
46
```typescript
47
import { defineRule } from "vee-validate";
48
49
// Simple validation rule
50
defineRule('required', (value: any) => {
51
if (!value || (Array.isArray(value) && value.length === 0)) {
52
return 'This field is required';
53
}
54
return true;
55
});
56
57
// Rule with parameters
58
defineRule('min', (value: string | number, [min]: [number]) => {
59
if (!value) return true; // Don't validate empty values
60
61
const length = typeof value === 'string' ? value.length : value;
62
if (length < min) {
63
return `This field must be at least ${min} characters`;
64
}
65
return true;
66
});
67
68
// Rule with multiple parameters
69
defineRule('between', (value: number, [min, max]: [number, number]) => {
70
if (value == null) return true;
71
72
if (value < min || value > max) {
73
return `This field must be between ${min} and ${max}`;
74
}
75
return true;
76
});
77
78
// Advanced rule with form context
79
defineRule('password_confirmation', (value: string, [], ctx) => {
80
const password = ctx.form.password;
81
82
if (value !== password) {
83
return 'Password confirmation does not match';
84
}
85
return true;
86
});
87
88
// Async validation rule
89
defineRule('unique_email', async (value: string) => {
90
if (!value) return true;
91
92
try {
93
const response = await fetch(`/api/check-email?email=${value}`);
94
const { available } = await response.json();
95
96
return available || 'This email is already taken';
97
} catch (error) {
98
return 'Unable to verify email availability';
99
}
100
});
101
102
// Rule with custom error messages based on parameters
103
defineRule('phone', (value: string, [format]: [string] = ['US']) => {
104
if (!value) return true;
105
106
const patterns = {
107
US: /^\(\d{3}\) \d{3}-\d{4}$/,
108
UK: /^\+44 \d{4} \d{6}$/,
109
INTERNATIONAL: /^\+\d{1,3} \d{1,14}$/
110
};
111
112
const pattern = patterns[format as keyof typeof patterns] || patterns.US;
113
114
if (!pattern.test(value)) {
115
return `Please enter a valid ${format} phone number`;
116
}
117
return true;
118
});
119
120
// Complex validation with multiple conditions
121
defineRule('strong_password', (value: string) => {
122
if (!value) return true;
123
124
const errors: string[] = [];
125
126
if (value.length < 8) {
127
errors.push('at least 8 characters');
128
}
129
130
if (!/[A-Z]/.test(value)) {
131
errors.push('one uppercase letter');
132
}
133
134
if (!/[a-z]/.test(value)) {
135
errors.push('one lowercase letter');
136
}
137
138
if (!/\d/.test(value)) {
139
errors.push('one number');
140
}
141
142
if (!/[!@#$%^&*(),.?":{}|<>]/.test(value)) {
143
errors.push('one special character');
144
}
145
146
if (errors.length > 0) {
147
return `Password must contain ${errors.join(', ')}`;
148
}
149
150
return true;
151
});
152
153
// Conditional validation rule
154
defineRule('required_if', (value: any, [targetField, targetValue]: [string, any], ctx) => {
155
const fieldValue = ctx.form[targetField];
156
157
if (fieldValue === targetValue && !value) {
158
return `This field is required when ${targetField} is ${targetValue}`;
159
}
160
161
return true;
162
});
163
```
164
165
### configure Function
166
167
Sets global validation configuration that affects all forms and fields.
168
169
```typescript { .api }
170
/**
171
* Sets global validation configuration
172
* @param config - Configuration object with validation settings
173
*/
174
function configure(config: Partial<VeeValidateConfig>): void;
175
176
interface VeeValidateConfig {
177
bails?: boolean; // Stop validation on first error
178
generateMessage?: ValidationMessageGenerator; // Custom message generator function
179
validateOnInput?: boolean; // Validate on input events
180
validateOnChange?: boolean; // Validate on change events
181
validateOnBlur?: boolean; // Validate on blur events
182
validateOnModelUpdate?: boolean; // Validate on v-model updates
183
}
184
185
type ValidationMessageGenerator = (ctx: ValidationMessageGeneratorContext) => string;
186
187
interface ValidationMessageGeneratorContext {
188
field: string; // Field name
189
name: string; // Field display name
190
label?: string; // Field label
191
value: unknown; // Field value
192
form: Record<string, unknown>; // Form values
193
rule: { // Rule information
194
name: string; // Rule name
195
params?: Record<string, unknown> | unknown[]; // Rule parameters
196
};
197
}
198
```
199
200
**configure Examples:**
201
202
```typescript
203
import { configure } from "vee-validate";
204
205
// Basic configuration
206
configure({
207
validateOnBlur: true,
208
validateOnChange: false,
209
validateOnInput: false,
210
validateOnModelUpdate: true,
211
bails: true
212
});
213
214
// Custom message generator
215
configure({
216
generateMessage: (ctx) => {
217
const messages: Record<string, string> = {
218
required: `${ctx.label || ctx.field} is required`,
219
email: `${ctx.label || ctx.field} must be a valid email`,
220
min: `${ctx.label || ctx.field} must be at least ${ctx.rule.params?.[0]} characters`,
221
max: `${ctx.label || ctx.field} must not exceed ${ctx.rule.params?.[0]} characters`,
222
confirmed: `${ctx.label || ctx.field} confirmation does not match`
223
};
224
225
return messages[ctx.rule.name] || `${ctx.label || ctx.field} is invalid`;
226
}
227
});
228
229
// Internationalization support
230
const messages = {
231
en: {
232
required: (field: string) => `${field} is required`,
233
email: (field: string) => `${field} must be a valid email`,
234
min: (field: string, params: any[]) => `${field} must be at least ${params[0]} characters`
235
},
236
es: {
237
required: (field: string) => `${field} es requerido`,
238
email: (field: string) => `${field} debe ser un email válido`,
239
min: (field: string, params: any[]) => `${field} debe tener al menos ${params[0]} caracteres`
240
}
241
};
242
243
const currentLocale = ref('en');
244
245
configure({
246
generateMessage: (ctx) => {
247
const locale = messages[currentLocale.value as keyof typeof messages];
248
const generator = locale[ctx.rule.name as keyof typeof locale];
249
250
if (generator) {
251
return generator(ctx.label || ctx.field, ctx.rule.params || []);
252
}
253
254
return `${ctx.label || ctx.field} is invalid`;
255
}
256
});
257
258
// Performance optimization configuration
259
configure({
260
// Only validate on blur to reduce validation calls
261
validateOnInput: false,
262
validateOnChange: false,
263
validateOnBlur: true,
264
265
// Stop on first error to improve performance
266
bails: true
267
});
268
269
// Development vs production configuration
270
const isDevelopment = process.env.NODE_ENV === 'development';
271
272
configure({
273
validateOnInput: isDevelopment, // More responsive validation in development
274
validateOnBlur: true,
275
bails: !isDevelopment, // Show all errors in development
276
277
generateMessage: (ctx) => {
278
if (isDevelopment) {
279
// Detailed error messages in development
280
return `[${ctx.rule.name}] ${ctx.field}: ${ctx.value} failed validation`;
281
}
282
283
// User-friendly messages in production
284
return getProductionMessage(ctx);
285
}
286
});
287
288
const getProductionMessage = (ctx: ValidationMessageGeneratorContext): string => {
289
// Production-friendly error messages
290
const field = ctx.label || humanizeFieldName(ctx.field);
291
292
switch (ctx.rule.name) {
293
case 'required':
294
return `Please enter your ${field.toLowerCase()}`;
295
case 'email':
296
return `Please enter a valid ${field.toLowerCase()}`;
297
case 'min':
298
return `${field} is too short`;
299
case 'max':
300
return `${field} is too long`;
301
default:
302
return `Please check your ${field.toLowerCase()}`;
303
}
304
};
305
306
const humanizeFieldName = (field: string): string => {
307
return field
308
.replace(/([A-Z])/g, ' $1') // Add space before capital letters
309
.replace(/^./, str => str.toUpperCase()) // Capitalize first letter
310
.replace(/_/g, ' '); // Replace underscores with spaces
311
};
312
```
313
314
## Built-in Rule Integration
315
316
### Using defineRule with Popular Validation Libraries
317
318
Integrating VeeValidate with existing validation rule libraries.
319
320
```typescript
321
import { defineRule } from "vee-validate";
322
323
// Integration with validator.js
324
import validator from "validator";
325
326
defineRule('email', (value: string) => {
327
if (!value) return true;
328
return validator.isEmail(value) || 'Please enter a valid email address';
329
});
330
331
defineRule('url', (value: string) => {
332
if (!value) return true;
333
return validator.isURL(value) || 'Please enter a valid URL';
334
});
335
336
defineRule('credit_card', (value: string) => {
337
if (!value) return true;
338
return validator.isCreditCard(value) || 'Please enter a valid credit card number';
339
});
340
341
// Custom business rules
342
defineRule('business_email', (value: string) => {
343
if (!value) return true;
344
345
const personalDomains = [
346
'gmail.com', 'yahoo.com', 'hotmail.com',
347
'outlook.com', 'aol.com', 'icloud.com'
348
];
349
350
const domain = value.split('@')[1];
351
352
if (personalDomains.includes(domain)) {
353
return 'Please use a business email address';
354
}
355
356
return validator.isEmail(value) || 'Please enter a valid email address';
357
});
358
359
// File validation rules
360
defineRule('file_size', (files: FileList | File[], [maxSize]: [number]) => {
361
if (!files || files.length === 0) return true;
362
363
const fileArray = Array.from(files);
364
const oversizedFiles = fileArray.filter(file => file.size > maxSize);
365
366
if (oversizedFiles.length > 0) {
367
const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(1);
368
return `File size must not exceed ${maxSizeMB}MB`;
369
}
370
371
return true;
372
});
373
374
defineRule('file_type', (files: FileList | File[], allowedTypes: string[]) => {
375
if (!files || files.length === 0) return true;
376
377
const fileArray = Array.from(files);
378
const invalidFiles = fileArray.filter(file =>
379
!allowedTypes.some(type => file.type.startsWith(type))
380
);
381
382
if (invalidFiles.length > 0) {
383
return `Only ${allowedTypes.join(', ')} files are allowed`;
384
}
385
386
return true;
387
});
388
389
// Date validation rules
390
defineRule('date_after', (value: string, [afterDate]: [string]) => {
391
if (!value) return true;
392
393
const inputDate = new Date(value);
394
const compareDate = new Date(afterDate);
395
396
if (inputDate <= compareDate) {
397
return `Date must be after ${compareDate.toLocaleDateString()}`;
398
}
399
400
return true;
401
});
402
403
defineRule('date_before', (value: string, [beforeDate]: [string]) => {
404
if (!value) return true;
405
406
const inputDate = new Date(value);
407
const compareDate = new Date(beforeDate);
408
409
if (inputDate >= compareDate) {
410
return `Date must be before ${compareDate.toLocaleDateString()}`;
411
}
412
413
return true;
414
});
415
416
// Age validation
417
defineRule('min_age', (birthDate: string, [minAge]: [number]) => {
418
if (!birthDate) return true;
419
420
const birth = new Date(birthDate);
421
const today = new Date();
422
const age = today.getFullYear() - birth.getFullYear();
423
const monthDiff = today.getMonth() - birth.getMonth();
424
425
const actualAge = monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())
426
? age - 1
427
: age;
428
429
if (actualAge < minAge) {
430
return `You must be at least ${minAge} years old`;
431
}
432
433
return true;
434
});
435
```
436
437
## Rule Usage Patterns
438
439
### String-based Rule Usage
440
441
Using defined rules in field validation.
442
443
```typescript
444
import { useField } from "vee-validate";
445
446
// Single rule
447
const { value: email } = useField('email', 'required');
448
449
// Multiple rules with pipe syntax
450
const { value: password } = useField('password', 'required|min:8|strong_password');
451
452
// Rules with parameters
453
const { value: age } = useField('age', 'required|between:18,100');
454
455
// Complex rule combinations
456
const { value: businessEmail } = useField(
457
'businessEmail',
458
'required|business_email'
459
);
460
461
// File upload validation
462
const { value: avatar } = useField(
463
'avatar',
464
'required|file_size:2097152|file_type:image/jpeg,image/png'
465
);
466
```
467
468
### Object-based Rule Usage
469
470
Using rules with object syntax for complex parameter passing.
471
472
```typescript
473
import { useField } from "vee-validate";
474
475
// Object rule syntax
476
const { value: username } = useField('username', {
477
required: true,
478
min: 3,
479
unique_email: true
480
});
481
482
// Mixed rule formats
483
const { value: phoneNumber } = useField('phoneNumber', [
484
'required',
485
{ phone: 'US' },
486
(value) => value.startsWith('+1') || 'Phone number must include country code'
487
]);
488
```
489
490
### Conditional Rule Application
491
492
Applying rules based on dynamic conditions.
493
494
```typescript
495
import { useField, useFormContext } from "vee-validate";
496
497
const form = useFormContext();
498
499
// Conditional validation based on other field values
500
const { value: confirmPassword } = useField(
501
'confirmPassword',
502
computed(() => {
503
const password = form.values.password;
504
return password ? 'required|password_confirmation' : '';
505
})
506
);
507
508
// Dynamic rule parameters
509
const { value: discountCode } = useField(
510
'discountCode',
511
computed(() => {
512
const userTier = form.values.userTier;
513
const rules = ['required'];
514
515
if (userTier === 'premium') {
516
rules.push('min:8');
517
} else {
518
rules.push('min:4');
519
}
520
521
return rules.join('|');
522
})
523
);
524
```