CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vee-validate

Painless forms for Vue.js with comprehensive validation, composition API, and component-based approaches.

Pending
Overview
Eval results
Files

configuration-rules.mddocs/

Configuration and Rules

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.

Capabilities

defineRule Function

Registers a custom validation rule globally that can be used throughout the application.

/**
 * Registers a custom validation rule globally
 * @param id - Unique rule identifier
 * @param validator - Rule implementation function
 */
function defineRule<TValue = unknown, TParams extends any[] = any[]>(
  id: string,
  validator: ValidationRuleFunction<TValue, TParams> | SimpleValidationRuleFunction<TValue, TParams>
): void;

type ValidationRuleFunction<TValue, TParams extends any[]> = (
  value: TValue,
  params: TParams,
  ctx: FieldValidationMetaInfo
) => MaybePromise<boolean | string>;

type SimpleValidationRuleFunction<TValue, TParams extends any[]> = (
  value: TValue,
  ...params: TParams
) => MaybePromise<boolean | string>;

interface FieldValidationMetaInfo {
  field: string;                                   // Field name
  name: string;                                    // Field display name
  label?: string;                                  // Field label
  form: Record<string, unknown>;                   // Current form values
  rule?: {                                         // Rule information
    name: string;                                  // Rule name
    params?: Record<string, unknown> | unknown[];  // Rule parameters
  };
}

defineRule Examples:

import { defineRule } from "vee-validate";

// Simple validation rule
defineRule('required', (value: any) => {
  if (!value || (Array.isArray(value) && value.length === 0)) {
    return 'This field is required';
  }
  return true;
});

// Rule with parameters
defineRule('min', (value: string | number, [min]: [number]) => {
  if (!value) return true; // Don't validate empty values
  
  const length = typeof value === 'string' ? value.length : value;
  if (length < min) {
    return `This field must be at least ${min} characters`;
  }
  return true;
});

// Rule with multiple parameters
defineRule('between', (value: number, [min, max]: [number, number]) => {
  if (value == null) return true;
  
  if (value < min || value > max) {
    return `This field must be between ${min} and ${max}`;
  }
  return true;
});

// Advanced rule with form context
defineRule('password_confirmation', (value: string, [], ctx) => {
  const password = ctx.form.password;
  
  if (value !== password) {
    return 'Password confirmation does not match';
  }
  return true;
});

// Async validation rule
defineRule('unique_email', async (value: string) => {
  if (!value) return true;
  
  try {
    const response = await fetch(`/api/check-email?email=${value}`);
    const { available } = await response.json();
    
    return available || 'This email is already taken';
  } catch (error) {
    return 'Unable to verify email availability';
  }
});

// Rule with custom error messages based on parameters
defineRule('phone', (value: string, [format]: [string] = ['US']) => {
  if (!value) return true;
  
  const patterns = {
    US: /^\(\d{3}\) \d{3}-\d{4}$/,
    UK: /^\+44 \d{4} \d{6}$/,
    INTERNATIONAL: /^\+\d{1,3} \d{1,14}$/
  };
  
  const pattern = patterns[format as keyof typeof patterns] || patterns.US;
  
  if (!pattern.test(value)) {
    return `Please enter a valid ${format} phone number`;
  }
  return true;
});

// Complex validation with multiple conditions
defineRule('strong_password', (value: string) => {
  if (!value) return true;
  
  const errors: string[] = [];
  
  if (value.length < 8) {
    errors.push('at least 8 characters');
  }
  
  if (!/[A-Z]/.test(value)) {
    errors.push('one uppercase letter');
  }
  
  if (!/[a-z]/.test(value)) {
    errors.push('one lowercase letter');
  }
  
  if (!/\d/.test(value)) {
    errors.push('one number');
  }
  
  if (!/[!@#$%^&*(),.?":{}|<>]/.test(value)) {
    errors.push('one special character');
  }
  
  if (errors.length > 0) {
    return `Password must contain ${errors.join(', ')}`;
  }
  
  return true;
});

// Conditional validation rule
defineRule('required_if', (value: any, [targetField, targetValue]: [string, any], ctx) => {
  const fieldValue = ctx.form[targetField];
  
  if (fieldValue === targetValue && !value) {
    return `This field is required when ${targetField} is ${targetValue}`;
  }
  
  return true;
});

configure Function

Sets global validation configuration that affects all forms and fields.

/**
 * Sets global validation configuration
 * @param config - Configuration object with validation settings
 */
function configure(config: Partial<VeeValidateConfig>): void;

interface VeeValidateConfig {
  bails?: boolean;                                 // Stop validation on first error
  generateMessage?: ValidationMessageGenerator;    // Custom message generator function
  validateOnInput?: boolean;                       // Validate on input events
  validateOnChange?: boolean;                      // Validate on change events
  validateOnBlur?: boolean;                        // Validate on blur events
  validateOnModelUpdate?: boolean;                 // Validate on v-model updates
}

type ValidationMessageGenerator = (ctx: ValidationMessageGeneratorContext) => string;

interface ValidationMessageGeneratorContext {
  field: string;                                   // Field name
  name: string;                                    // Field display name
  label?: string;                                  // Field label
  value: unknown;                                  // Field value
  form: Record<string, unknown>;                   // Form values
  rule: {                                          // Rule information
    name: string;                                  // Rule name
    params?: Record<string, unknown> | unknown[];  // Rule parameters
  };
}

configure Examples:

import { configure } from "vee-validate";

// Basic configuration
configure({
  validateOnBlur: true,
  validateOnChange: false,
  validateOnInput: false,
  validateOnModelUpdate: true,
  bails: true
});

// Custom message generator
configure({
  generateMessage: (ctx) => {
    const messages: Record<string, string> = {
      required: `${ctx.label || ctx.field} is required`,
      email: `${ctx.label || ctx.field} must be a valid email`,
      min: `${ctx.label || ctx.field} must be at least ${ctx.rule.params?.[0]} characters`,
      max: `${ctx.label || ctx.field} must not exceed ${ctx.rule.params?.[0]} characters`,
      confirmed: `${ctx.label || ctx.field} confirmation does not match`
    };
    
    return messages[ctx.rule.name] || `${ctx.label || ctx.field} is invalid`;
  }
});

// Internationalization support
const messages = {
  en: {
    required: (field: string) => `${field} is required`,
    email: (field: string) => `${field} must be a valid email`,
    min: (field: string, params: any[]) => `${field} must be at least ${params[0]} characters`
  },
  es: {
    required: (field: string) => `${field} es requerido`,
    email: (field: string) => `${field} debe ser un email válido`,
    min: (field: string, params: any[]) => `${field} debe tener al menos ${params[0]} caracteres`
  }
};

const currentLocale = ref('en');

configure({
  generateMessage: (ctx) => {
    const locale = messages[currentLocale.value as keyof typeof messages];
    const generator = locale[ctx.rule.name as keyof typeof locale];
    
    if (generator) {
      return generator(ctx.label || ctx.field, ctx.rule.params || []);
    }
    
    return `${ctx.label || ctx.field} is invalid`;
  }
});

// Performance optimization configuration
configure({
  // Only validate on blur to reduce validation calls
  validateOnInput: false,
  validateOnChange: false,
  validateOnBlur: true,
  
  // Stop on first error to improve performance
  bails: true
});

// Development vs production configuration
const isDevelopment = process.env.NODE_ENV === 'development';

configure({
  validateOnInput: isDevelopment,  // More responsive validation in development
  validateOnBlur: true,
  bails: !isDevelopment,          // Show all errors in development
  
  generateMessage: (ctx) => {
    if (isDevelopment) {
      // Detailed error messages in development
      return `[${ctx.rule.name}] ${ctx.field}: ${ctx.value} failed validation`;
    }
    
    // User-friendly messages in production
    return getProductionMessage(ctx);
  }
});

const getProductionMessage = (ctx: ValidationMessageGeneratorContext): string => {
  // Production-friendly error messages
  const field = ctx.label || humanizeFieldName(ctx.field);
  
  switch (ctx.rule.name) {
    case 'required':
      return `Please enter your ${field.toLowerCase()}`;
    case 'email':
      return `Please enter a valid ${field.toLowerCase()}`;
    case 'min':
      return `${field} is too short`;
    case 'max':
      return `${field} is too long`;
    default:
      return `Please check your ${field.toLowerCase()}`;
  }
};

const humanizeFieldName = (field: string): string => {
  return field
    .replace(/([A-Z])/g, ' $1')  // Add space before capital letters
    .replace(/^./, str => str.toUpperCase())  // Capitalize first letter
    .replace(/_/g, ' ');  // Replace underscores with spaces
};

Built-in Rule Integration

Using defineRule with Popular Validation Libraries

Integrating VeeValidate with existing validation rule libraries.

import { defineRule } from "vee-validate";

// Integration with validator.js
import validator from "validator";

defineRule('email', (value: string) => {
  if (!value) return true;
  return validator.isEmail(value) || 'Please enter a valid email address';
});

defineRule('url', (value: string) => {
  if (!value) return true;
  return validator.isURL(value) || 'Please enter a valid URL';
});

defineRule('credit_card', (value: string) => {
  if (!value) return true;
  return validator.isCreditCard(value) || 'Please enter a valid credit card number';
});

// Custom business rules
defineRule('business_email', (value: string) => {
  if (!value) return true;
  
  const personalDomains = [
    'gmail.com', 'yahoo.com', 'hotmail.com', 
    'outlook.com', 'aol.com', 'icloud.com'
  ];
  
  const domain = value.split('@')[1];
  
  if (personalDomains.includes(domain)) {
    return 'Please use a business email address';
  }
  
  return validator.isEmail(value) || 'Please enter a valid email address';
});

// File validation rules
defineRule('file_size', (files: FileList | File[], [maxSize]: [number]) => {
  if (!files || files.length === 0) return true;
  
  const fileArray = Array.from(files);
  const oversizedFiles = fileArray.filter(file => file.size > maxSize);
  
  if (oversizedFiles.length > 0) {
    const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(1);
    return `File size must not exceed ${maxSizeMB}MB`;
  }
  
  return true;
});

defineRule('file_type', (files: FileList | File[], allowedTypes: string[]) => {
  if (!files || files.length === 0) return true;
  
  const fileArray = Array.from(files);
  const invalidFiles = fileArray.filter(file => 
    !allowedTypes.some(type => file.type.startsWith(type))
  );
  
  if (invalidFiles.length > 0) {
    return `Only ${allowedTypes.join(', ')} files are allowed`;
  }
  
  return true;
});

// Date validation rules
defineRule('date_after', (value: string, [afterDate]: [string]) => {
  if (!value) return true;
  
  const inputDate = new Date(value);
  const compareDate = new Date(afterDate);
  
  if (inputDate <= compareDate) {
    return `Date must be after ${compareDate.toLocaleDateString()}`;
  }
  
  return true;
});

defineRule('date_before', (value: string, [beforeDate]: [string]) => {
  if (!value) return true;
  
  const inputDate = new Date(value);
  const compareDate = new Date(beforeDate);
  
  if (inputDate >= compareDate) {
    return `Date must be before ${compareDate.toLocaleDateString()}`;
  }
  
  return true;
});

// Age validation
defineRule('min_age', (birthDate: string, [minAge]: [number]) => {
  if (!birthDate) return true;
  
  const birth = new Date(birthDate);
  const today = new Date();
  const age = today.getFullYear() - birth.getFullYear();
  const monthDiff = today.getMonth() - birth.getMonth();
  
  const actualAge = monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate()) 
    ? age - 1 
    : age;
  
  if (actualAge < minAge) {
    return `You must be at least ${minAge} years old`;
  }
  
  return true;
});

Rule Usage Patterns

String-based Rule Usage

Using defined rules in field validation.

import { useField } from "vee-validate";

// Single rule
const { value: email } = useField('email', 'required');

// Multiple rules with pipe syntax
const { value: password } = useField('password', 'required|min:8|strong_password');

// Rules with parameters
const { value: age } = useField('age', 'required|between:18,100');

// Complex rule combinations
const { value: businessEmail } = useField(
  'businessEmail', 
  'required|business_email'
);

// File upload validation
const { value: avatar } = useField(
  'avatar',
  'required|file_size:2097152|file_type:image/jpeg,image/png'
);

Object-based Rule Usage

Using rules with object syntax for complex parameter passing.

import { useField } from "vee-validate";

// Object rule syntax
const { value: username } = useField('username', {
  required: true,
  min: 3,
  unique_email: true
});

// Mixed rule formats
const { value: phoneNumber } = useField('phoneNumber', [
  'required',
  { phone: 'US' },
  (value) => value.startsWith('+1') || 'Phone number must include country code'
]);

Conditional Rule Application

Applying rules based on dynamic conditions.

import { useField, useFormContext } from "vee-validate";

const form = useFormContext();

// Conditional validation based on other field values
const { value: confirmPassword } = useField(
  'confirmPassword',
  computed(() => {
    const password = form.values.password;
    return password ? 'required|password_confirmation' : '';
  })
);

// Dynamic rule parameters
const { value: discountCode } = useField(
  'discountCode',
  computed(() => {
    const userTier = form.values.userTier;
    const rules = ['required'];
    
    if (userTier === 'premium') {
      rules.push('min:8');
    } else {
      rules.push('min:4');
    }
    
    return rules.join('|');
  })
);

Install with Tessl CLI

npx tessl i tessl/npm-vee-validate

docs

configuration-rules.md

core-validation.md

field-management.md

form-actions.md

form-management.md

index.md

state-access.md

vue-components.md

tile.json