Rilaykit Logorilaykit ✨
Forms

Field & Form Validation

Learn how to add validation rules to your forms.

Rilay comes with a powerful and extensible validation system. You can add validation rules at both the field level and the form level.

Field-Level Validation

You can specify validation rules directly on a field when you add it to the form builder.

The validation property in the FieldConfig object accepts the following:

  • validators: An array of validator functions.
  • validateOnChange: Validate the field whenever its value changes.
  • validateOnBlur: Validate the field when the user leaves it (on blur).
  • debounceMs: Debounce validation by a specified number of milliseconds.
import { rilay } from '@/lib/rilay';
import { required, minLength, email } from '@rilaykit/core';

const registrationForm = rilay
  .form('registration')
  .add({
    id: 'email',
    type: 'email',
    props: { label: 'Email Address' },
    validation: {
      validators: [required(), email('Please enter a valid email address.')],
      validateOnBlur: true,
    },
  })
  .add({
    id: 'password',
    type: 'password',
    props: { label: 'Password' },
    validation: {
      validators: [required(), minLength(8)],
      validateOnChange: true,
    },
  });

Built-in Validators

Rilay provides a set of common validators out of the box, imported from @rilaykit/core:

  • required(message?): Ensures the field is not empty.
  • minLength(min, message?): Checks for a minimum string length.
  • maxLength(max, message?): Checks for a maximum string length.
  • pattern(regex, message?): Validates against a regular expression.
  • email(message?): Validates for a correct email format.
  • url(message?): Validates for a valid URL format.
  • number(message?): Checks if the value is a valid number.
  • min(minValue, message?): Checks for a minimum numeric value.
  • max(maxValue, message?): Checks for a maximum numeric value.
  • custom(fn, message, code?): Creates a custom synchronous validator.
  • async(fn, message, code?): Creates a custom asynchronous validator.
  • matchField(fieldId, message?): Checks if the value matches another field's value.
  • when(condition, validator): Applies a validator conditionally.

Custom Validators

You can easily create your own reusable validators. A validator is a function that receives the value and a context object, and returns a ValidationResult.

import type { ValidationResult, ValidationContext } from '@rilaykit/core';

export function containsRilay(message = 'The value must contain "rilay"') {
  return (value: string, context: ValidationContext): ValidationResult => {
    if (typeof value !== 'string' || !value.includes('rilay')) {
      return { 
        isValid: false, 
        errors: [{ message, code: 'DOES_NOT_CONTAIN_RILAY' }] 
      };
    }
    return { isValid: true, errors: [] };
  };
}

Form-Level Validation

Sometimes you need to validate multiple fields together. You can use the setValidation method on the form builder to add form-level validators. These validators run when the form is submitted.

import { rilay } from '@/lib/rilay';
import { createErrorResult, createSuccessResult } from '@rilaykit/core';

const changePasswordForm = rilay
  .form('change-password')
  .add({ type: 'password', id: 'newPassword', props: { label: 'New Password' } })
  .add({ type: 'password', id: 'confirmPassword', props: { label: 'Confirm Password' } })
  .setValidation({
    validators: [
      (formData) => {
        if (formData.newPassword !== formData.confirmPassword) {
          return createErrorResult("Passwords don't match", 'PASSWORD_MISMATCH');
        }
        return createSuccessResult();
      },
    ],
  });

Using Zod Schemas

If you prefer using Zod for validation, Rilay provides an adapter to seamlessly integrate Zod schemas.

You can use zodFieldValidator to create a field validator from a Zod schema.

import { z } from 'zod';
import { zodFieldValidator } from '@rilaykit/core';

const emailField = {
  id: 'email',
  type: 'email',
  props: { label: 'Email' },
  validation: {
    validators: [
      zodFieldValidator(z.string().email('Invalid email from Zod'))
    ],
  },
};

Use zodFormValidator for form-level validation with a Zod object schema.

import { z } from 'zod';
import { zodFormValidator } from '@rilaykit/core';

const formSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

const myForm = rilay.form('zod-form')
  .add(...)
  .setValidation({
    validators: [zodFormValidator(formSchema)],
  });

The Zod adapter is just one example. You can create your own adapter for any validation library by implementing the ValidationAdapter interface.