Skip to main content

Command Palette

Search for a command to run...

6 Open Source Form Validation Libraries Every Frontend Developer Should Know

A practical comparison of form validation libraries for modern JavaScript applications

Published

Form validation is one of those problems that looks simple until you try to implement it well. Client-side validation needs to feel instant without being annoying. Server-side validation needs to match the client-side rules exactly. Error messages need to be accessible, localizable, and clear. Validation state needs to be testable, debuggable, and portable across frameworks. What starts as "just check if the email field is filled in" becomes an architectural decision that affects the entire form experience.

These six libraries each take a different approach to the validation problem. Some prioritize developer ergonomics. Others prioritize performance. Some are framework-agnostic. Others are tightly integrated with specific UI libraries. I have used each of these in production, and the trade-offs become clear once you understand what each tool was designed to optimize for.

1. Zod

Zod is a TypeScript-first schema validation library that has quickly become the default choice for type-safe form validation. You define schemas using a fluent API, and Zod infers TypeScript types automatically, so your validation rules and your TypeScript types stay in sync.

import { z } from 'zod';

const SignupSchema = z.object({
  email: z.string().email('Please enter a valid email'),
  password: z.string()
    .min(8, 'Password must be at least 8 characters')
    .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
    .regex(/[0-9]/, 'Password must contain at least one number'),
  age: z.number().min(18, 'You must be 18 or older').max(120),
  termsAccepted: z.literal(true, {
    errorMap: () => ({ message: 'You must accept the terms to continue' })
  })
});

type SignupForm = z.infer<typeof SignupSchema>;

const result = SignupSchema.safeParse(formData);
if (!result.success) {
  console.log(result.error.issues);
}

Zod is strongest when combined with form libraries like React Hook Form or TanStack Form that accept Zod schemas as resolvers. The Zod documentation is excellent, with examples for every common validation pattern including nested objects, discriminated unions, and transformations.

The trade-off is bundle size. A basic Zod import adds around 12KB gzipped to your application. For projects where bundle size is critical, lighter alternatives exist.

Developer writing TypeScript code for form validation on a laptop Photo by Daniil Komov on Pexels

2. Yup

Yup predates Zod and remains widely used in JavaScript codebases that do not rely on TypeScript. Its API is similar to Zod but was designed around object schemas rather than type inference.

import * as Yup from 'yup';

const ContactSchema = Yup.object({
  name: Yup.string().required('Name is required'),
  email: Yup.string().email('Invalid email').required('Email is required'),
  message: Yup.string()
    .min(10, 'Message is too short')
    .max(500, 'Message is too long')
    .required('Message is required')
});

await ContactSchema.validate(formData, { abortEarly: false });

Yup integrates naturally with Formik, which is still the most popular form library for React applications that prefer controlled components. For teams that do not need TypeScript integration, Yup is a solid choice with a smaller bundle than Zod.

3. Valibot

Valibot takes a fundamentally different approach to bundle size. Instead of a monolithic library, every validation is a separate function that you import individually. Modern bundlers tree-shake unused functions, so your final bundle contains only the specific validators you used.

import * as v from 'valibot';

const LoginSchema = v.object({
  email: v.pipe(v.string(), v.email('Please enter a valid email')),
  password: v.pipe(v.string(), v.minLength(8, 'Password too short'))
});

const result = v.safeParse(LoginSchema, formData);

The bundle size difference is significant. A basic Valibot form schema typically adds 2-3KB gzipped compared to Zod's 12KB. For mobile-first applications or performance-critical applications, that difference matters. The Valibot documentation includes migration guides for developers coming from Zod or Yup.

The trade-off is API ergonomics. The explicit pipe syntax is more verbose than Zod's method chaining. For teams that prioritize developer experience over bundle size, Zod often feels smoother. For teams shipping to constrained environments, Valibot's approach pays off.

4. React Hook Form (with Built-in Validation)

React Hook Form deserves a place on this list because it includes validation capabilities beyond just integrating with schema libraries. Its register API supports built-in validation rules that work without importing a separate schema library.

import { useForm } from 'react-hook-form';

function SignupForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('email', {
          required: 'Email is required',
          pattern: {
            value: /^[^@]+@[^@]+\.[^@]+$/,
            message: 'Please enter a valid email'
          }
        })}
      />
      {errors.email && <span>{errors.email.message}</span>}
    </form>
  );
}

React Hook Form minimizes re-renders by using uncontrolled components, which makes it significantly faster than alternatives for large forms. The React Hook Form documentation includes extensive examples for complex scenarios including conditional fields, field arrays, and nested forms.

"Form validation is where user experience meets engineering reality. The library you pick should solve the validation problem without introducing new problems. Performance matters, developer experience matters, and bundle size matters - usually not in that order." - Dennis Traina, 137Foundry

5. VeeValidate

VeeValidate is the most mature form validation library in the Vue ecosystem. It provides template-based validation (using directives in your HTML) and composable-based validation (using Vue's composition API) in the same package.

<template>
  <Form @submit="onSubmit" :validation-schema="schema">
    <Field name="email" type="email" />
    <ErrorMessage name="email" />
    <Field name="password" type="password" />
    <ErrorMessage name="password" />
  </Form>
</template>

<script setup>
import { Form, Field, ErrorMessage } from 'vee-validate';
import * as yup from 'yup';

const schema = yup.object({
  email: yup.string().email().required(),
  password: yup.string().min(8).required()
});
</script>

VeeValidate accepts Yup or Zod schemas and integrates with Vue's reactivity system naturally. For Vue applications, this is the standard choice with the best documentation and widest community support. The VeeValidate GitHub repository shows consistent maintenance and regular releases.

6. Joi

Joi started as a server-side validation library for Node.js and has since expanded to work in browser environments. Its API is similar to Yup but with more extensive built-in validators for common patterns like URIs, IP addresses, credit card numbers, and JWT tokens.

const Joi = require('joi');

const ProfileSchema = Joi.object({
  username: Joi.string().alphanum().min(3).max(30).required(),
  email: Joi.string().email().required(),
  phone: Joi.string().pattern(/^[0-9]{10}$/),
  website: Joi.string().uri().optional(),
  birthYear: Joi.number().integer().min(1900).max(2010)
});

const { error, value } = ProfileSchema.validate(formData);

Joi is strongest when you want to share the exact same validation logic between your Node.js backend and your frontend. Deploy the schema to both environments and ensure validation stays consistent. The trade-off is bundle size in browser environments, where Joi is significantly larger than Zod or Valibot. For backend-heavy applications where most validation happens server-side, this is often an acceptable trade-off. The Joi documentation covers every built-in validator and the custom rule API for domain-specific validation logic.

Designer and developer collaborating on form UI design at a computer Photo by Ofspace LLC, Culture on Pexels

Choosing Between Them

Match the library to your specific constraints:

  • TypeScript project, developer experience priority: Zod
  • JavaScript project or small bundles critical: Yup or Valibot
  • Vue application: VeeValidate
  • Shared validation with a Node.js backend: Joi
  • High-performance React forms: React Hook Form's built-in validation or React Hook Form + Zod

For a broader guide on designing forms that convert well, including UX patterns, validation timing, and common mistakes that hurt completion rates, this guide on designing web forms that users actually complete covers the principles that should inform your validation implementation. The team at 137Foundry regularly helps clients pair validation libraries with UX patterns that reduce form abandonment.

Further Reading

The right validation library is the one that matches your framework, your TypeScript situation, and your bundle size constraints. For most new React projects, Zod plus React Hook Form is the path of least resistance. For performance-critical applications, Valibot is worth the extra setup. For Vue applications, VeeValidate remains the clear choice.