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 🙏

© 2025 – Pkg Stats / Ryan Hefner

izgood

v3.1.1

Published

Client side validation with React hooks

Readme

izgood

Dog approves

Check if data izgood in three simple steps! ✨

  1. Use React hook const [validate, { ErrorMessage }] = useValidationLazy(rules)
  2. Place the <ErrorMessage /> components where you want the error messages to appear
  3. Use the validate(formdata) callback before submitting your form to the API. It returns false if data iz not good. Also, beautiful error messages suddenly starts appearing in your form 💅

No dependencies and tiny bundle size (1.4kB minified + gzipped)! 🧑‍💻

Contents

Examples

A simple sign up form

import { izEmail, izMoreThan, izNotEmpty, useValidationLazy } from "izgood";
import { useCallback } from "react";

export default function SignUpForm() {
  const [validate, { ErrorMessage }] = useValidationLazy([
    ["firstName", izNotEmpty, "Please enter your first name"],
    ["lastName", izNotEmpty, "Please enter your last name"],
    ["email", izNotEmpty, "Please enter your email"],
    ["email", izEmail, "Please enter a valid email"],
    ["age", izMoreThan(13), "You have to be 13 years or older to sign up"],
  ]);

  const handleSubmit = useCallback(
    (e) => {
      e.preventDefault();

      const formdata = new FormData(e.target);
      const data = Object.fromEntries(formdata);
      if (!validate(data)) return;

      // ...POST request hidden for brevity
    },
    [validate]
  );

  return (
    <form onSubmit={handleSubmit}>
      <h1>Sign up! 📝</h1>

      <input name="firstName" />
      <ErrorMessage name="firstName" />

      <input name="lastName" />
      <ErrorMessage name="lastName" />

      <input name="nickName" />

      <input name="email" />
      <ErrorMessage name="email" onlyFirstError />

      <input name="age" type="number" />
      <ErrorMessage name="age" />

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

Styling the <ErrorMessage /> component

The <ErrorMessage /> component is just a <div>, so any <div> HTML attribute can be passed to <ErrorMessage />. This means that you can style it however you want.

  • Inline: <ErrorMessage style={{ background: 'red' }} />
  • CSS Modules: <ErrorMessage className={styles.errorMessage} />
  • Global styling using built in class .izgood-error { background: red; }
  • Using Tailwind's @layer directive by adding this pattern to Tailwind config.content: "./node_modules/izgood/**/*.{js,ts,jsx,tsx}"

Validating nested properties in data

The simplest approach is to wrap the validator function like so:

const data = {
  user: {
    email: "[email protected]",
  },
};

const validationResult = useValidation(data, [
  ["user", (u) => izEmail(u.email), "Please enter a valid email address"],
]);

This will however put the errors on "user" instead of "user.email" (because rule.name === "user").

If you want more granular error message selection when validating nested properties, you could also go for a more modular approach by creating reusable validation hooks. The validate function returned from useValidationLazy can in fact be used as a rule inside another useValidationLazy hook, like this:

// The object we want to validate (a money transaction)
const transactionData = {
  sender: {
    email: "[email protected]",
    fullName: "John Doe",
    age: 29,
  },
  receiver: {
    email: "[email protected]",
    fullName: "Jane Smith",
    age: 38,
  },
  value: 100,
};

// Custom hook for validating user objects (both sender and receiver)
const useUserValidationLazy = () =>
  useValidationLazy([
    ["email", izEmail],
    ["age", izMoreThan(13), "User has to be 13 years or older!"],
  ]);

// MyTransactionForm.tsx
import { useValidationLazy, izNotEmpty, izMoreThan } from "izgood";

export default function MyTransactionForm({ transactionData }) {
  const [izSenderGood, { ErrorMessage: SenderErrorMessage }] =
    useUserValidationLazy();
  const [izReceiverGood, { ErrorMessage: ReceiverErrorMessage }] =
    useUserValidationLazy();

  const [validate, { ErrorMessage: TransactionErrorMessage }] =
    useValidationLazy([
      ["sender", izSenderGood], // <--  The `validate` functions returned from useUserValidationLazy
      ["receiver", izReceiverGood], //  used as a rule in this useValidationLazy hook 💁‍♂️ 🪄
      ["value", izMoreThan(0), "Value must be more than 0"],
    ]);

  // Run the validation, causing the nested validation modules to be executed as well
  const handleValidate = useCallback(() => {
    validate(transactionData);
  }, [validate, transactionData]);

  return (
    <div>
      <button onClick={handleValidate}>Validate</button>
      <SenderErrorMessage name="email" /> {/* <-- This will only render errors on "sender.email" 💡 */}
      {/* Other render logic hidden for brevity */}
    </div>
  );
}

This way, you can render <SenderErrorMessage name="email" /> to precisely get the error message on "sender.email", instead of bundling up all messages in the same <TransactionErrorMessage name="sender" />, rendering both "age" and "email" errors.

API Reference 📝

type Rules

type ValidationRule = {
  name: string; // Name of the value (eg. "email" or "firstName")
  validator: ValidatorFunction; // The function checking the value
  errorMessage?: string; // Error message if invalid
};
type ValidatorFunction<T> = (value: T) => boolean;

// ...or use a tuple for more shorthand syntax 🎨
type ValidationRuleTuple = [string, ValidatorFunction, string?];

type Rules = (ValidationRule | ValidationRuleTuple)[];

izgood comes with some built-in validator functions:

  • izNotEmpty,
  • izEmail
  • izMoreThan(min: number)
  • ...you can easily extend this list by creating your own function returning
    • true on valid input
    • false on invalid input.

function useValidation()

function useValidation(
  data: any,
  rules: ValidationRule[]
): {
  ErrorMessage: (props: ErrorMessageProps) => JSX.Element;
  hasErrors: (name?: string) => boolean;
  getErrors: (name?: string) => string[];
};

function useValidationLazy()

function useValidationLazy(rules: ValidationRule[]): [
  validate: (data: any, name?: string) => boolean,
  {
    ErrorMessage: (props: ErrorMessageProps) => JSX.Element;
    hasErrors: (name?: string) => boolean;
    getErrors: (name?: string) => string[];
  }
];
  • Specify a name: string on validation to only validate a single part of the data