ForgeForm

Schema

ForgeForm's schema declaration is at the heart of its validation power. Let's explore various schema examples showcasing different field types, built-in validations, and custom validators (both synchronous and asynchronous), as well as custom regex patterns directly within schemas.

Content

Basic Schema Declaration with Various Field Types

This example demonstrates a common schema structure, showcasing various built-in field types and basic validations like required, minLength, maxLength, min, max, and format (for email).

import { createSchema } from 'forgeform';
 
interface RegistrationFormData {
  username?: string;
  fullName?: string;
  email?: string;
  age?: number;
  password?: string;
  confirmPassword?: string;
  newsletter?: boolean;
}
 
const registrationSchema = createSchema<RegistrationFormData>({
  fields: {
    username: {
      type: 'string',
      required: true,
      minLength: 3,
      maxLength: 20,
      requiredErrorMessage: 'Username is required',
      minLengthErrorMessage: 'Username must be at least 3 characters',
      maxLengthErrorMessage: 'Username cannot exceed 20 characters',
    },
    fullName: {
      type: 'string',
      required: true,
      requiredErrorMessage: 'Full name is required',
    },
    email: {
      type: 'email',
      required: true,
      formatErrorMessage: 'Invalid email format',
      requiredErrorMessage: 'Email is required',
    },
    age: {
      type: 'number',
      min: 18,
      max: 120,
      minErrorMessage: 'Must be at least 18 years old',
      maxErrorMessage: 'Age cannot exceed 120 years',
    },
    password: {
      type: 'password',
      required: true,
      minLength: 8,
      requiredErrorMessage: 'Password is required',
      minLengthErrorMessage: 'Password must be at least 8 characters',
    },
    confirmPassword: {
      type: 'password', // No direct validation here, cross-field validation with custom validator (see later)
      required: true,
      requiredErrorMessage: 'Confirm password is required',
    },
    newsletter: {
      type: 'boolean', // Boolean type, often used for checkboxes
    },
  },
});
  • In this registrationSchema:

  • We define fields like username, fullName, email, age, password, etc., each with a specific type (e.g., 'string', 'email', 'number', 'boolean', 'password').

  • Common validation rules like required, minLength, maxLength, min, max, and formatErrorMessage are directly specified within each field's definition, making the schema declarative and easy to read.

  • Custom error messages (e.g., requiredErrorMessage, minLengthErrorMessage) are provided to make error feedback user-friendly.

Using Synchronous Custom Validators:

Synchronous validators are ideal for immediate, client-side validation logic. They are functions that receive the field value and optionally the entire form data. They should return an error message string if validation fails, or undefined if validation passes.

This example demonstrates a synchronous custom validator to ensure that confirmPassword field matches the password field:

import { createSchema } from 'forgeform';
 
interface PasswordMatchFormData {
  password?: string;
  confirmPassword?: string;
}
 
const passwordMatchSchema = createSchema<PasswordMatchFormData>({
  fields: {
    password: {
      type: 'password',
      required: true,
      minLength: 8,
      requiredErrorMessage: 'Password is required',
      minLengthErrorMessage: 'Password must be at least 8 characters',
    },
    confirmPassword: {
      type: 'password',
      required: true,
      requiredErrorMessage: 'Confirm password is required',
      customValidator: (value, formData) => {
        if (value!== formData?.password) {
          return 'Passwords do not match.'; // Error message if passwords don't match
        }
        return undefined; // Validation passes
      },
      customErrorMessage: 'Password confirmation error', // Optional custom error message key
    },
  },
});
  • In passwordMatchSchema:

  • We define a customValidator function for the confirmPassword field.

  • This validator function compares the confirmPassword value (value) with the password value from the entire formData.

  • If they don't match, it returns an error message string 'Passwords do not match.'.

  • If they match, it returns undefined, indicating successful validation.

Using Asynchronous Custom Validators:

  • Asynchronous validators are used for validations that require operations that might take time, such as checking username availability against a database or calling an API. They are functions that return a Promise which resolves to an error message string (if invalid) or undefined (if valid).

This example shows an asynchronous validator to check if a username is already taken (simulating an API call):

import { createSchema } from 'forgeform';
 
interface UniqueUsernameFormData {
  username?: string;
}
 
// Simulating an asynchronous username availability check (replace with actual API call)
const checkUsernameAvailability = async (username: string): Promise<boolean> => {
  await new Promise(resolve => setTimeout(resolve, 500)); // Simulate API delay
  const takenUsernames = ['takenUsername', 'anotherTakenName']; // Example taken usernames
  return takenUsernames.includes(username); // Returns true if username is taken
};
 
const uniqueUsernameSchema = createSchema<UniqueUsernameFormData>({
  fields: {
    username: {
      type: 'string',
      required: true,
      minLength: 3,
      requiredErrorMessage: 'Username is required',
      minLengthErrorMessage: 'Username must be at least 3 characters',
      customValidator: async (value) => {
        const isTaken = await checkUsernameAvailability(value);
        if (isTaken) {
          return 'Username is already taken. Please choose another.'; // Error message if username is taken
        }
        return undefined; // Validation passes
      },
      customErrorMessage: 'Username availability check failed', // Optional custom error message key
    },
  },
});

In uniqueUsernameSchema:

  • We define an asynchronous function checkUsernameAvailability (simulating an API call).
  • The customValidator for the username field is also an async function.
  • It calls checkUsernameAvailability(value) and awaits the result.
  • If checkUsernameAvailability returns true (username taken), the validator returns an error message.
  • Otherwise, it returns undefined for successful validation.

Using Custom Regex Patterns in Schema:

While ForgeForm provides over 50 built-in regex patterns through field types like email, url, tel, etc., you can also directly embed custom regular expressions within your schema for more specific format validations.

This example demonstrates using a custom regex to validate a product code format:

import { createSchema } from 'forgeform';
 
interface ProductCodeFormData {
  productCode?: string;
}
 
const productCodeSchema = createSchema<ProductCodeFormData>({
  fields: {
    productCode: {
      type: 'string',
      required: true,
      pattern: /^[A-Z]{3}-\d{4}$/, // Custom regex: 3 uppercase letters, hyphen, 4 digits
      requiredErrorMessage: 'Product code is required',
      patternErrorMessage: 'Invalid product code format. Example: ABC-1234',
    },
  },
});

In productCodeSchema:

  • We use the pattern attribute for the productCode field.
  • The value of pattern is a regular expression literal: /^[A-Z]{3}-\d{4}$/. This regex enforces a format of three uppercase letters, followed by a hyphen, and then four digits.
  • patternErrorMessage provides a custom error message specifically for regex validation failures.

Combining Validators and Rules

You can freely combine built-in validation rules, synchronous custom validators, asynchronous custom validators, and custom regex patterns within a single schema to create robust and tailored validation logic for your forms. For instance, you can have a field that is required, has a minLength, and also uses a customValidator for more complex business logic.

On this page