Using Lodash conformsTo for Form Validation

This article provides an overview of how the Lodash _.conformsTo method can be applied to web form validation. By mapping form field values to specific validator functions, developers can perform declarative, schema-like checks across an entire form payload. Below, we explore practical use cases, code examples, and the operational benefits of using this method to handle client-side form logic.

Understanding _.conformsTo

The _.conformsTo method checks if an object conforms to a source object of predicate functions. It returns true if all predicate functions return truthy values for corresponding properties in the target object, and false otherwise:

_.conformsTo(object, source);

In form validation, the object represents the user's input data, while the source acts as a validation schema where keys match input names and values are validation rules.


1. Single-Step Registration and Signup Forms

The most common use case is validating standard user registration fields—such as usernames, email addresses, and passwords—before submitting the payload.

import _ from 'lodash';

const formData = {
  username: 'dev_user',
  email: 'user@example.com',
  age: 22,
  termsAccepted: true
};

const registrationSchema = {
  username: (val) => typeof val === 'string' && val.trim().length >= 3,
  email: (val) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),
  age: (val) => Number.isInteger(val) && val >= 18,
  termsAccepted: (val) => val === true
};

const isFormValid = _.conformsTo(formData, registrationSchema);

Using _.conformsTo ensures that every field meets its criteria in a single declarative call, avoiding long chains of if-else or nested boolean operators.


2. Multi-Step (Wizard) Forms

In multi-step checkout or onboarding flows, validating the current step before allowing the user to proceed is critical. You can define separate schema objects for each step and evaluate them dynamically.

const wizardData = {
  step1: { fullName: 'Jane Doe', phone: '1234567890' },
  step2: { shippingAddress: '123 Main St', postalCode: '90210' }
};

const stepValidators = {
  1: {
    fullName: (val) => Boolean(val && val.length > 0),
    phone: (val) => /^\d{10}$/.test(val)
  },
  2: {
    shippingAddress: (val) => Boolean(val && val.length > 5),
    postalCode: (val) => /^\d{5}$/.test(val)
  }
};

function canProceedToNextStep(currentStepNumber, data) {
  const currentValidator = stepValidators[currentStepNumber];
  return _.conformsTo(data, currentValidator);
}

This approach encapsulates step-specific rules and makes adding, removing, or reordering steps straightforward.


3. Real-Time Submit Button State Management

Disabling a submit button until all required inputs are valid is a common UI pattern. Because _.conformsTo returns a boolean immediately, it can run efficiently inside reactive frameworks on every input change.

function onInputChange(updatedFormData) {
  const submitButton = document.querySelector('#submit-btn');
  
  const rules = {
    cardNumber: (val) => /^\d{16}$/.test(val),
    cvv: (val) => /^\d{3,4}$/.test(val),
    expiryMonth: (val) => val >= 1 && val <= 12
  };

  submitButton.disabled = !_.conformsTo(updatedFormData, rules);
}

This ensures fast evaluations without the overhead of instantiating heavy validation libraries on simple forms.


4. Conditional and Role-Based Validation

Enterprise applications often adjust required fields based on the user's role or selected choices (e.g., business accounts vs. individual accounts). Validation schemas can be composed modularly.

const baseAccountRules = {
  email: (val) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),
  password: (val) => typeof val === 'string' && val.length >= 8
};

const corporateAccountRules = {
  ...baseAccountRules,
  taxId: (val) => /^[0-9]{9}$/.test(val),
  companyName: (val) => typeof val === 'string' && val.length > 2
};

function validateAccountCreation(formData, isCorporate) {
  const rules = isCorporate ? corporateAccountRules : baseAccountRules;
  return _.conformsTo(formData, rules);
}

By leveraging object spread syntax, rules can be inherited and combined cleanly depending on runtime conditions.


Limitations to Consider

While _.conformsTo is lightweight and clear, it has specific constraints in form validation:

For complex forms needing detailed per-field error messages, consider combining _.conformsTo with custom field error trackers, or reserving it for high-level submission guards.