Back to docsRecipe

Form Validation Patterns

A collection of reusable validation strategies for React forms — from simple required-field checks to cross-field rules and async uniqueness validators.

Required field

The simplest rule — reject empty strings, null, or whitespace-only values. Always trim before checking.

const required = (v: string) =>
  v.trim().length > 0 ? null : "Required";

Min / max length

Compose length checks into a single validator factory. Return the first failure or null.

const lengthBetween = (min: number, max: number) =>
  (v: string) => {
    if (v.length < min) return `Min ${min} chars`;
    if (v.length > max) return `Max ${max} chars`;
    return null;
  };

Cross-field equality

Validate that two fields match — password confirmation is the classic case. Accept the sibling value as a parameter.

const matches = (other: string) =>
  (v: string) => v === other ? null : "Fields must match";

Async uniqueness

Debounce remote checks and surface server errors. Always abort in-flight requests when the input changes.

const unique = (endpoint: string) => {
  let ctrl: AbortController;
  return async (v: string) => {
    ctrl?.abort();
    ctrl = new AbortController();
    const res = await fetch(endpoint, {
      method: "POST",
      body: JSON.stringify({ value: v }),
      signal: ctrl.signal,
    });
    return res.ok ? null : "Already taken";
  };
};