npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-smart-form-errors

v2.0.3

Published

A lightweight, production-ready form validation library for React with 15+ built-in validators and zero dependencies

Downloads

703

Readme

React Smart Form Errors

A lightweight, production-ready form validation library for React. Zero dependencies, full TypeScript support, and comprehensive validation rules out of the box.

Features

15+ Built-in Validators - email, phone, password, DOB, URLs, and more ✅ Lightweight - No external dependencies, tiny bundle size ✅ TypeScript Support - Complete type definitions included ✅ Flexible - String rules, object rules with options, or custom functions ✅ Framework Agnostic - Works with React useState, React Hook Form, or Formik ✅ Accessible - Full form state management with touched/untouched tracking ✅ Customizable Messages - Override default error messages globally or per-field ✅ 90%+ Test Coverage - Comprehensive test suite with Vitest

Installation

npm install react-smart-form-errors

Quick Start

import { useSmartForm } from 'react-smart-form-errors';

function RegistrationForm() {
  const form = useSmartForm({
    initialValues: {
      email: '',
      password: '',
      fullName: '',
      phone: '',
      dob: ''
    },
    rules: {
      email: ['required', 'email'],
      password: ['required', { rule: 'password', minLength: 8 }],
      fullName: ['required', 'fullname'],
      phone: ['required', 'phone'],
      dob: ['required', { rule: 'dob', minAge: 18 }]
    }
  });

  const handleSubmit = (e) => {
    e.preventDefault();
    if (form.validateForm()) {
      console.log('Form is valid!', form.values);
      // Submit form
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          name="email"
          type="email"
          placeholder="Email"
          value={form.values.email}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
        />
        {form.getFieldError('email') && (
          <span className="error">{form.getFieldError('email')}</span>
        )}
      </div>

      <div>
        <input
          name="password"
          type="password"
          placeholder="Password"
          value={form.values.password}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
        />
        {form.getFieldError('password') && (
          <span className="error">{form.getFieldError('password')}</span>
        )}
      </div>

      <div>
        <input
          name="fullName"
          placeholder="Full Name"
          value={form.values.fullName}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
        />
        {form.getFieldError('fullName') && (
          <span className="error">{form.getFieldError('fullName')}</span>
        )}
      </div>

      <div>
        <input
          name="phone"
          type="tel"
          placeholder="Phone"
          value={form.values.phone}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
        />
        {form.getFieldError('phone') && (
          <span className="error">{form.getFieldError('phone')}</span>
        )}
      </div>

      <div>
        <input
          name="dob"
          type="date"
          value={form.values.dob}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
        />
        {form.getFieldError('dob') && (
          <span className="error">{form.getFieldError('dob')}</span>
        )}
      </div>

      <button type="submit" disabled={!form.isValid}>
        Register
      </button>
    </form>
  );
}

Hook API

useSmartForm(config)

Configuration

const form = useSmartForm({
  // Initial form values
  initialValues: {
    email: '',
    password: '',
    fullName: '',
    phone: '',
    dob: ''
  },
  
  // Validation rules - string, array, object, or function
  rules: {
    email: ['required', 'email'],
    password: ['required', { rule: 'password', minLength: 8 }],
    fullName: ['required', 'fullname'],
    phone: ['required', 'phone'],
    dob: ['required', { rule: 'dob', minAge: 18 }]
  },
  
  // Custom error messages (optional)
  messages: {
    required: (field) => `${field} is required`,
    email: (field) => `${field} is not a valid email`,
    // ... override other messages
  }
});

Return Object

{
  // Form values
  values,           // Current form values
  errors,           // Object with field errors
  touched,          // Fields that have been interacted with
  
  // Validation methods
  validateField,    // Validate a single field
  validateForm,     // Validate all fields
  getFieldError,    // Get error message for a field (returns string)
  
  // Value setters
  setValue,         // Set single field value
  setValues,        // Set multiple field values
  setError,         // Set error manually
  setTouched,       // Set touched state
  resetForm,        // Reset to initial state
  
  // Event handlers
  handleChange,     // Handle input changes
  handleBlur,       // Handle field blur
  
  // Computed state
  isValid,          // True if no errors
  isDirty           // True if values differ from initialValues
}

Event Handlers

handleChange

Supports both React events and manual usage:

// React event handler
<input onChange={form.handleChange} />

// Manual usage
form.handleChange('email', '[email protected]');

handleBlur

Supports both React events and manual usage:

// React event handler
<input onBlur={form.handleBlur} />

// Manual usage
form.handleBlur('email');

Import Validators

You can import and use validators directly:

import {
  useSmartForm,
  validateEmail,
  validatePhone,
  validatePassword,
  validateDOB,
  validateFullName,
  validateRequired
} from 'react-smart-form-errors';

// Use in custom validation
const customEmail = validateEmail('[email protected]');
const customPhone = validatePhone('03001234567');

#### `phone`
Validates phone numbers (supports Pakistani format by default: 03001234567, +923001234567, 923001234567).

```javascript
rules: {
  phone: 'phone',
}

Text Validators

fullName

Validates full names (must have at least first and last name).

rules: {
  fullName: 'fullName',
}

firstName

Validates first names.

rules: {
  firstName: 'firstName',
}

lastName

Validates last names.

rules: {
  lastName: 'lastName',
}

username

Validates usernames (alphanumeric and underscore only).

rules: {
  username: [
    'required',
    { rule: 'username', minLength: 3, maxLength: 20 },
  ],
}

Password Validators

password

Validates password strength (requires uppercase, lowercase, number, and special character).

rules: {
  password: [
    'required',
    { rule: 'password', minLength: 8 },
  ],
}

confirmPassword

Validates that a password matches another field.

rules: {
  password: ['required', { rule: 'password', minLength: 8 }],
  confirmPassword: [
    'required',
    { rule: 'confirmPassword', password: values.password },
  ],
}

Specialized Validators

dob (Date of Birth)

Validates date of birth with optional minimum age check.

rules: {
  dob: [
    'required',
    { rule: 'dob', minAge: 18 },
  ],
}

Supports YYYY-MM-DD format or Date objects.

url

Validates HTTP/HTTPS URLs.

rules: {
  website: 'url',
}

number

Validates numeric values with optional min/max constraints.

rules: {
  age: [
    'required',
    { rule: 'number', min: 0, max: 150 },
  ],
}

Length & Pattern Validators

minLength

Validates minimum string length.

rules: {
  bio: ['required', { rule: 'minLength', length: 10 }],
}

maxLength

Validates maximum string length.

rules: {
  title: { rule: 'maxLength', length: 100 },
}

pattern

Validates against a regular expression.

rules: {
  zipCode: {
    rule: 'pattern',
    regex: /^\d{5}$/,
  },
}

Hook API

useSmartForm(config)

Parameters

{
  initialValues?: Record<string, any>,
  rules?: ValidationRulesMap,
  messages?: ErrorMessages,
}

Return Value

{
  // State
  values: Record<string, any>,
  errors: Record<string, ValidationError>,
  touched: Record<string, boolean>,

  // Validation methods
  validateField: (fieldName, value) => ValidationError | null,
  validateForm: () => boolean,
  getFieldError: (fieldName) => string | null,

  // State setters
  setValue: (fieldName, value) => void,
  setError: (fieldName, error) => void,
  setValues: (values) => void,
  setTouched: (touched) => void,
  resetForm: () => void,

  // Event handlers
  handleChange: (e: ChangeEvent) => void,
  handleBlur: (e: FocusEvent) => void,

  // Computed state
  isValid: boolean,
  isDirty: boolean,
}

Direct Validator Imports

You can also import validators directly without the hook:

import {
  validateEmail,
  validatePhone,
  validatePassword,
  validateDOB,
  validateFullName,
  validateUsername,
  validateURL,
  validateNumber,
} from 'react-smart-form-errors';

const error = validateEmail('[email protected]');
// error will be null if valid, or an error object if invalid

Error Messages

Default Messages

The library comes with sensible default error messages:

{
  required: (field) => `${field} is required`,
  email: (field) => `${field} must be a valid email address`,
  phone: (field) => `${field} must be a valid phone number`,
  password: {
    minLength: (field, value) => `${field} must be at least ${value} characters`,
    uppercase: (field) => `${field} must contain at least one uppercase letter`,
    lowercase: (field) => `${field} must contain at least one lowercase letter`,
    number: (field) => `${field} must contain at least one number`,
    special: (field) => `${field} must contain at least one special character`,
  },
  // ... more messages
}

Custom Messages

Override messages globally or per-validation:

import { useSmartForm, messages } from 'react-smart-form-errors';

const { values, errors, ... } = useSmartForm({
  initialValues: { email: '' },
  rules: { email: ['required', 'email'] },
  messages: {
    ...messages,
    required: (field) => `Please fill in ${field}`,
    email: (field) => `Please provide a valid ${field.toLowerCase()}`,
  },
});

Examples

React with useState

import { useSmartForm } from 'react-smart-form-errors';

function SignupForm() {
  const form = useSmartForm({
    initialValues: {
      email: '',
      password: '',
      fullName: '',
      phone: '',
      dob: '',
    },
    rules: {
      email: ['required', 'email'],
      password: ['required', { rule: 'password', minLength: 8 }],
      fullName: ['required', 'fullName'],
      phone: ['required', 'phone'],
      dob: ['required', { rule: 'dob', minAge: 18 }],
    },
  });

  const handleSubmit = (e) => {
    e.preventDefault();
    if (form.validateForm()) {
      console.log('Signup:', form.values);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          name="email"
          type="email"
          value={form.values.email}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
          placeholder="Email"
        />
        {form.touched.email && form.errors.email && (
          <span>{form.getFieldError('email')}</span>
        )}
      </div>

      <div>
        <input
          name="password"
          type="password"
          value={form.values.password}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
          placeholder="Password"
        />
        {form.touched.password && form.errors.password && (
          <span>{form.getFieldError('password')}</span>
        )}
      </div>

      <div>
        <input
          name="fullName"
          value={form.values.fullName}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
          placeholder="Full Name"
        />
        {form.touched.fullName && form.errors.fullName && (
          <span>{form.getFieldError('fullName')}</span>
        )}
      </div>

      <div>
        <input
          name="phone"
          value={form.values.phone}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
          placeholder="Phone"
        />
        {form.touched.phone && form.errors.phone && (
          <span>{form.getFieldError('phone')}</span>
        )}
      </div>

      <div>
        <input
          name="dob"
          type="date"
          value={form.values.dob}
          onChange={form.handleChange}
          onBlur={form.handleBlur}
        />
        {form.touched.dob && form.errors.dob && (
          <span>{form.getFieldError('dob')}</span>
        )}
      </div>

      <button type="submit" disabled={!form.isValid && form.isDirty}>
        Sign Up
      </button>
    </form>
  );
}

export default SignupForm;

React Hook Form Integration

import { useForm } from 'react-hook-form';
import { validators } from 'react-smart-form-errors';

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

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('email', {
          validate: (value) => {
            const error = validators.email(value);
            return error ? 'Invalid email' : true;
          },
        })}
      />
      {errors.email && <span>{errors.email.message}</span>}

      <input
        {...register('password', {
          validate: (value) => {
            const error = validators.password(value, { minLength: 8 });
            return error ? 'Password too weak' : true;
          },
        })}
      />

      <button type="submit">Submit</button>
    </form>
  );
}

Formik Integration

import { Formik, Form, Field, ErrorMessage } from 'formik';
import { validateEmail, validatePhone, validatePassword } from 'react-smart-form-errors';

const validationSchema = {
  email: (value) => validateEmail(value),
  phone: (value) => validatePhone(value),
  password: (value) => validatePassword(value, { minLength: 8 }),
};

function MyForm() {
  return (
    <Formik
      initialValues={{ email: '', phone: '', password: '' }}
      validate={(values) => {
        const errors = {};
        Object.keys(values).forEach((key) => {
          const error = validationSchema[key]?.(values[key]);
          if (error) errors[key] = error.type;
        });
        return errors;
      }}
      onSubmit={(values) => console.log(values)}
    >
      <Form>
        <Field name="email" type="email" />
        <ErrorMessage name="email" component="div" />

        <Field name="phone" />
        <ErrorMessage name="phone" component="div" />

        <Field name="password" type="password" />
        <ErrorMessage name="password" component="div" />

        <button type="submit">Submit</button>
      </Form>
    </Formik>
  );
}

Custom Validators

import { useSmartForm } from 'react-smart-form-errors';

function MyForm() {
  const customEvenNumber = (value) => {
    const num = Number(value);
    if (isNaN(num) || num % 2 !== 0) {
      return { type: 'evenNumber' };
    }
    return null;
  };

  const form = useSmartForm({
    initialValues: { number: '' },
    rules: {
      number: ['required', customEvenNumber],
    },
    messages: {
      evenNumber: (field) => `${field} must be an even number`,
    },
  });

  return (
    <div>
      <input
        name="number"
        value={form.values.number}
        onChange={form.handleChange}
      />
      {form.errors.number && <div>{form.getFieldError('number')}</div>}
    </div>
  );
}

TypeScript Usage

import { useSmartForm, ValidationRulesMap, UseSmartFormReturn } from 'react-smart-form-errors';

interface FormValues {
  email: string;
  password: string;
  fullName: string;
}

const rules: ValidationRulesMap = {
  email: ['required', 'email'],
  password: ['required', { rule: 'password', minLength: 8 }],
  fullName: ['required', 'fullName'],
};

function MyForm(): JSX.Element {
  const form: UseSmartFormReturn = useSmartForm({
    initialValues: {
      email: '',
      password: '',
      fullName: '',
    },
    rules,
  });

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      if (form.validateForm()) {
        const data: FormValues = form.values as FormValues;
        console.log(data);
      }
    }}>
      {/* Form fields */}
    </form>
  );
}

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

Performance

  • Bundle Size: ~5KB gzipped
  • Zero Dependencies: No external libraries
  • Tree-shakeable: Import only what you need

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please open an issue on GitHub.

import { useFormErrors } from 'react-smart-form-errors';

function MyForm() {
    const { errors, validate, clearErrors } = useFormErrors();

    const handleSubmit = async (formData) => {
        if (validate(formData)) {
            // Submit form
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            <input name="email" />
            {errors.email && <span>{errors.email}</span>}
        </form>
    );
}

Benefits

  • ✅ Reduce boilerplate code for error handling
  • ✅ Improve user experience with instant validation feedback
  • ✅ Maintain clean, readable component code
  • ✅ Reusable validation logic across your application
  • ✅ Better accessibility and error messaging

Documentation

See docs for complete API reference and examples.

License

MIT Shaeel Khan