Validation

FormForge validates on blur and on submit. Errors appear below each field after the user has touched it.

Built-in rules

import { rules } from '@formforges/validator'

const schema = {
  email:    { type: 'email',    validate: rules.email() },
  username: { type: 'text',     validate: rules.minLength(3) },
  age:      { type: 'number',   validate: rules.min(18) },
  site:     { type: 'text',     validate: rules.url() },
  code:     { type: 'text',     validate: rules.pattern(/^[A-Z]{3}$/) },
}

Rule reference

RuleUsageDescription
requiredrules.required()Field must have a value
emailrules.email()Must be a valid email
minLengthrules.minLength(8)Minimum character count
maxLengthrules.maxLength(100)Maximum character count
minrules.min(0)Minimum numeric value
maxrules.max(999)Maximum numeric value
patternrules.pattern(/regex/)Must match regex
urlrules.url()Must be a valid URL
oneOfrules.oneOf(['a','b'])Must be one of the values

Custom message

validate: rules.minLength(8, 'Password must be at least 8 characters')

Multiple validators

password: {
  type: 'password',
  validate: [
    rules.required(),
    rules.minLength(8),
    rules.pattern(/[A-Z]/, 'Must contain uppercase letter'),
    rules.pattern(/[0-9]/, 'Must contain a number'),
  ],
}

Custom validator

// Return error string or null
validate: (value, allValues) => {
  if (value !== allValues.password) {
    return 'Passwords do not match'
  }
  return null
}

Async validation

username: {
  type: 'text',
  validate: async (value) => {
    const res = await fetch(`/api/check-username?q=${value}`)
    const { taken } = await res.json()
    return taken ? 'Username already taken' : null
  },
}

Zod adapter

import { zodValidator } from '@formforges/validator'
import { z } from 'zod'

const schema = {
  phone: {
    type: 'text',
    validate: zodValidator(
      z.string().regex(/^+?[ds]{7,}$/, 'Invalid phone number')
    ),
  },
}