Core Concepts
Validation

Validation

better-form provides built-in validation rules and supports custom validators.

Required Fields

{
  id: 'email',
  name: 'email',
  label: 'Email',
  type: 'text',
  required: true,
  requiredMessage: 'Email is required', // Custom message
}

Built-in Rules

Min/Max Length (text)

{
  id: 'username',
  name: 'username',
  type: 'text',
  validation: {
    minLength: 3,
    minLengthMessage: 'Username must be at least 3 characters',
    maxLength: 20,
    maxLengthMessage: 'Username cannot exceed 20 characters',
  },
}

Min/Max Value (number)

{
  id: 'age',
  name: 'age',
  type: 'number',
  validation: {
    min: 18,
    minMessage: 'Must be at least 18 years old',
    max: 120,
    maxMessage: 'Please enter a valid age',
  },
}

Pattern (regex)

{
  id: 'email',
  name: 'email',
  type: 'text',
  validation: {
    pattern: '^[^@]+@[^@]+\\.[^@]+$',
    patternMessage: 'Please enter a valid email address',
  },
}

Custom Validation

Use the customValidation property for complex rules:

{
  id: 'password',
  name: 'password',
  type: 'text',
  customValidation: (value, formData) => {
    if (!value) return 'Password is required';
    if (value.length < 8) return 'Password must be at least 8 characters';
    if (!/[A-Z]/.test(value)) return 'Password must contain uppercase letter';
    if (!/[0-9]/.test(value)) return 'Password must contain a number';
    return undefined; // Valid
  },
}

Cross-Field Validation

Validate fields based on other field values:

{
  id: 'confirmPassword',
  name: 'confirmPassword',
  type: 'text',
  customValidation: (value, formData) => {
    if (value !== formData.password) {
      return 'Passwords do not match';
    }
    return undefined;
  },
}

Async Validation

Validate against external APIs:

{
  id: 'username',
  name: 'username',
  type: 'text',
  customValidation: async (value, formData) => {
    const response = await fetch(`/api/check-username?name=${value}`);
    const { available } = await response.json();
    if (!available) {
      return 'Username is already taken';
    }
    return undefined;
  },
}

Validation Timing

Validation runs:

  • On blur (when leaving a field)
  • On change (after first validation attempt)
  • On step navigation
  • On form submission