+1 (843) 212-6898+8801897661858
Whatsapp-colorCreated with Sketch.
sales@mediusware.comSchedule A Call
Logo
Company
Services
Hire Developers
Industries
Case StudySTART FREE TRIALicon

About

Who We Are

Our Team

Blogs

Women Empowerment

Career

CSR

Delivery Models

Engagement Model

Services

Software Development

Web Development

Mobile App Development

E-commerce Development

Software Development

Enterprise Solutions

UI/UX Design

API Integration

Technology

React.js

Vue.js

Angular js

Laravel

Android

Flutter

iOS

React Native

Hire Developers

Hire Mobile App Developers

Hire Front-end Developers

Hire Backend Developers

Hire E-commerce Developers

Hire Developers

Hire Android Developers

Hire iOS Developers

Hire Swift Developers

Hire Flutter Developers

Hire React Native Developers

Hire Django Developers

Hire Node.js Developers

Hire Laravel Developers

We shape the art of technology
Headquarter

Headquarter

1050 Johnnie Dodds Blvd Mount Pleasant South Carolina USA ,Zip- 29464

sales@mediusware.io

+1 843-212-7149

Bangladesh Office

Bangladesh Office

24/1, Taj Mahal Road, Shiya Masjid mor, Floor - 8th & 9th, Ring Road, 1207, Dhaka, Bangladesh

sales@mediusware.com

+8801897661858

© 2025 Mediusware. All Rights Reserved

Terms & ConditionsPrivacy Policy

Table of contents

  1. Introduction
  2. Installation
  3. Defining and Using Custom Validation Schemas
  4. Coercion in Zod: Automatically Turning Data into the Right Type
  5. Integrating Zod with React Hook Form for Enhanced Validation
Share This Blog !
Get the best of our content straight to your inbox!

Don’t worry, we don’t spam!

Introduction to Zod: A Validation Library for TypeScript

Introduction to Zod: A Validation Library for TypeScript image

Last Update: 12 Oct 2024

Introduction

Imagine you're working on an application that collects user information, and you want to ensure that every piece of data is correct. Zod is a tool that helps you with exactly that. When you're coding with TypeScript, Zod makes sure that the data coming in fits the rules you define.

In TypeScript, while types provide safety, they only work at compile time (when you're writing the code). Zod takes it a step further by actually validating the data at runtime, so when the app is running, you can be confident that your data is correct. Let’s explore how Zod helps you build more reliable applications! [documentation]

Installation

Requirement

  • TypeScript 4.5+!

  • You must enable strict mode in your tsconfig.json. This is a best practice for all TypeScript projects.
    // tsconfig.json
    {
      // ...
      "compilerOptions": {
        // ...
        "strict": true
      }
    }
  • From npm (Node/Bun)
    npm install zod       # npm
    yarn add zod          # yarn
    bun add zod           # bun
    pnpm add zod          # pnpm

Defining and Using Custom Validation Schemas

Creating a simple string schema:

import { z } from "zod";

// creating a schema for strings
const mySchema = z.string();

// parsing
mySchema.parse("tuna"); // => "tuna"
mySchema.parse(12); // => throws ZodError

// "safe" parsing (doesn't throw error if validation fails)
mySchema.safeParse("tuna"); // => { success: true; data: "tuna" }
mySchema.safeParse(12); // => { success: false; error: ZodError }

Number Validation:

import { z } from "zod";

// Creating a schema for numbers
const numberSchema = z.number();

// parsing
numberSchema.parse(42); // => 42
numberSchema.parse("42"); // => throws ZodError

// "safe" parsing
numberSchema.safeParse(42); // => { success: true; data: 42 }
numberSchema.safeParse("42"); // => { success: false; error: ZodError }

Boolean Validation:

import { z } from "zod";

// Creating a schema for booleans
const booleanSchema = z.boolean();

// parsing
booleanSchema.parse(true); // => true
booleanSchema.parse("true"); // => throws ZodError

// "safe" parsing
booleanSchema.safeParse(false); // => { success: true; data: false }
booleanSchema.safeParse("true"); // => { success: false; error: ZodError }

Array Validation:

import { z } from "zod";

// Creating a schema for an array of numbers
const numberArraySchema = z.array(z.number());

// parsing
numberArraySchema.parse([1, 2, 3]); // => [1, 2, 3]
numberArraySchema.parse([1, "two", 3]); // => throws ZodError

// "safe" parsing
numberArraySchema.safeParse([1, 2, 3]); // => { success: true; data: [1, 2, 3] }
numberArraySchema.safeParse([1, "two", 3]); // => { success: false; error: ZodError }

Object Validation:

import { z } from "zod";

// Creating a schema for an object with defined properties
const userSchema = z.object({
  name: z.string(),
  age: z.number(),
});

// parsing
userSchema.parse({ name: "Alice", age: 30 }); // => { name: "Alice", age: 30 }
userSchema.parse({ name: "Alice", age: "30" }); // => throws ZodError

// "safe" parsing
userSchema.safeParse({ name: "Alice", age: 30 }); // => { success: true; data: { name: "Alice", age: 30 } }
userSchema.safeParse({ name: "Alice", age: "30" }); // => { success: false; error: ZodError }

 Enum Validation:

import { z } from "zod";

// Creating a schema for an enum of string values
const roleSchema = z.enum(["admin", "user", "guest"]);

// parsing
roleSchema.parse("admin"); // => "admin"
roleSchema.parse("superadmin"); // => throws ZodError

// "safe" parsing
roleSchema.safeParse("user"); // => { success: true; data: "user" }
roleSchema.safeParse("superadmin"); // => { success: false; error: ZodError }

Optional Validation:

import { z } from "zod";

// Creating a schema where the value can be either string or undefined
const optionalSchema = z.string().optional();

// parsing
optionalSchema.parse("Hello"); // => "Hello"
optionalSchema.parse(undefined); // => undefined
optionalSchema.parse(42); // => throws ZodError

// "safe" parsing
optionalSchema.safeParse("Hello"); // => { success: true; data: "Hello" }
optionalSchema.safeParse(undefined); // => { success: true; data: undefined }
optionalSchema.safeParse(42); // => { success: false; error: ZodError }

Default Value for Undefined:

import { z } from "zod";

// Creating a schema where the default value is set if undefined
const defaultSchema = z.string().default("default value");

// parsing
defaultSchema.parse("Hi"); // => "Hi"
defaultSchema.parse(undefined); // => "default value"

// "safe" parsing
defaultSchema.safeParse(undefined); // => { success: true; data: "default value" }

More About Primitives Type Validation

import { z } from "zod";

// primitive values
z.string();
z.number();
z.bigint();
z.boolean();
z.date();
z.symbol();

// empty types
z.undefined();
z.null();
z.void(); // accepts undefined

// catch-all types
// allows any value
z.any();
z.unknown();

// never type
// allows no values
z.never();

 

 

Coercion in Zod: Automatically Turning Data into the Right Type

Coercion in Zod: Automatically Turning Data into the Right Type

In Zod, coercion is the process of automatically converting whatever data you have into the type you need. Instead of throwing an error when the type doesn’t match, Zod will attempt to "coerce" or convert the input into the expected type. It does this by using JavaScript’s built-in tools like String(), Number(), and Boolean() to transform the data.

Let’s break this down with some easy-to-understand examples.

String Coercion: Making Anything a String

When you use z.coerce.string(), Zod will try to turn any input into a string, just like JavaScript does when you use the String() function. For example:

z.coerce.string().parse(12); // => "12" (number 12 becomes the string "12")
z.coerce.string().parse(true); // => "true" (boolean true becomes the string "true")
z.coerce.string().parse(undefined); // => "undefined" (undefined becomes the string "undefined")
z.coerce.string().parse(null); // => "null" (null becomes the string "null")

Even if the input starts as a number, a boolean, or even null, Zod will try to make it fit the string type. This is super useful when you're handling data from users that might not always come in the format you expect.

You can also chain other methods to the string schema, like validating that the string is a valid email address or ensuring it meets a minimum length:

z.coerce.string().email().min(5);

 Number Coercion: Turning Values into Numbers

When you use z.coerce.number(), Zod tries to convert the input into a number using the Number() function in JavaScript. Here’s how it wor

z.coerce.number().parse("42"); // => 42 (the string "42" becomes the number 42)
z.coerce.number().parse(true); // => 1 (true becomes 1)
z.coerce.number().parse(false); // => 0 (false becomes 0)
z.coerce.number().parse(null); // => 0 (null becomes 0)

Zod will make a good attempt to turn anything you pass it into a number. For instance, "42" (a string) becomes the number 42, and true becomes 1. However, some inputs, like "abc", won’t convert cleanly into numbers, and you'll get NaN (Not a Number).

Boolean Coercion: Handling True/False Values

Boolean coercion in Zod works by converting any "truthy" value to true and any "falsy" value to false. In JavaScript:

  • Truthy values include things like non-empty strings, numbers greater than 0, or objects.
  • Falsy values include 0, empty strings, null, undefined, etc.

Here’s how Zod handles booleans:

const schema = z.coerce.boolean();

schema.parse("tuna"); // => true (non-empty string becomes true)
schema.parse("true"); // => true (non-empty string becomes true)
schema.parse("false"); // => true (non-empty string becomes true)
schema.parse(1); // => true (1 is truthy, so it becomes true)
schema.parse([]); // => true (even an empty array is truthy in JavaScript)

schema.parse(0); // => false (0 is falsy, so it becomes false)
schema.parse(""); // => false (empty string is falsy, so it becomes false)
schema.parse(undefined); // => false (undefined is falsy, so it becomes false)
schema.parse(null); // => false (null is falsy, so it becomes false)

A Word of Caution on Boolean Coercion

It’s important to understand that any truthy value becomes true. This means even strings like "false" or "no" will still be coerced to true because they’re non-empty strings. If you need to strictly check for true or false values, boolean coercion might not behave as expected, so keep that in mind.

Date Coercion: Converting Input into Dates:

When working with dates, you can use z.coerce.date() to automatically convert various inputs into a valid Date object. Zod uses the new Date() function from JavaScript to handle this conversion:

z.coerce.date().parse("2024-01-01"); // => new Date("2024-01-01")
z.coerce.date().parse(1672531200000); // => new Date("2023-01-01") (timestamp becomes a date)

 

 

Integrating Zod with React Hook Form for Enhanced Validation

In this section, let's walk through how you can use Zod and React Hook Form together to make form validation more powerful and flexible. We'll also see how to add some advanced validation like using refinement and regex for extra control.

What is React Hook Form?

React Hook Form (RHForm) is a library that helps manage forms in React. It handles input validation, form state, and error messages in a super simple way. By combining RHForm with Zod, we can create strong, easy-to-read validation logic.

Setting Up React Hook Form with Zod:
To integrate Zod with React Hook Form, you’ll need to install both libraries:

npm install react-hook-form zod @hookform/resolvers

Step 1: Create a Basic Form with Zod and React Hook Form

Let’s create a simple registration form that includes fields for name, email, and age. We’ll define a basic schema for our form:

import React from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';

// Define your Zod schema
const schema = z.object({
  name: z.string().min(2, "Name must be at least 2 characters long."),
  email: z.string().email("Please enter a valid email."),
  age: z.number().min(18, "You must be at least 18 years old.")
});

function RegistrationForm() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema)
  });

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>Name</label>
        <input {...register("name")} />
        {errors.name && <p>{errors.name.message}</p>}
      </div>
      <div>
        <label>Email</label>
        <input {...register("email")} />
        {errors.email && <p>{errors.email.message}</p>}
      </div>
      <div>
        <label>Age</label>
        <input type="number" {...register("age")} />
        {errors.age && <p>{errors.age.message}</p>}
      </div>
      <button type="submit">Register</button>
    </form>
  );
}

export default RegistrationForm;

Explanation:

  • useForm: Hook from React Hook Form for managing form state.
  • zodResolver: Connects Zod with React Hook Form for validation.
  • schema: Defines the validation rules for the form fields.

Step 2: Adding Refinement and Regex Validation

Now, let’s enhance our form validation using refinement and regex patterns.

Name Validation Using Regex

We’ll ensure that the name only contains alphabetic characters (no numbers or special characters). Here’s how we can use regex for that:

const schema = z.object({
  name: z.string()
    .min(2, "Name must be at least 2 characters long.")
    .regex(/^[A-Za-z\s]+$/, "Name should only contain letters and spaces."),
  email: z.string()
    .email("Please enter a valid email."),
  age: z.number()
    .min(18, "You must be at least 18 years old.")
});

regex(/^[A-Za-z\s]+$/): This regular expression checks that the name contains only letters (both upper and lower case) and spaces. If it contains anything else, the error message "Name should only contain letters and spaces." will be displayed.

Email Validation with Refinement

Next, let’s refine our email validation. We’ll restrict it to only accept emails ending with a specific domain, like @example.com:

const schema = z.object({
  name: z.string()
    .min(2, "Name must be at least 2 characters long.")
    .regex(/^[A-Za-z\s]+$/, "Name should only contain letters and spaces."),
  email: z.string()
    .email("Please enter a valid email.")
    .refine((value) => value.endsWith('@example.com'), {
      message: "Email must end with @example.com",
    }),
  age: z.number()
    .min(18, "You must be at least 18 years old.")
});

refine(): This method allows you to add custom logic for validation. Here, we check if the email ends with @example.com. If it doesn’t, the user will see the message "Email must end with @example.com."

Age Validation with Refinement:
Lastly, let’s add a rule to ensure the age is an even number:

const schema = z.object({
  name: z.string()
    .min(2, "Name must be at least 2 characters long.")
    .regex(/^[A-Za-z\s]+$/, "Name should only contain letters and spaces."),
  email: z.string()
    .email("Please enter a valid email.")
    .refine((value) => value.endsWith('@example.com'), {
      message: "Email must end with @example.com",
    }),
  age: z.number()
    .min(18, "You must be at least 18 years old.")
    .refine((value) => value % 2 === 0, {
      message: "Age must be an even number."
    })
});

refine(value % 2 === 0): This line checks if the age is even by checking if it’s divisible by 2. If the user enters an odd number, they will receive the message "Age must be an even number."

Step 3: Handling Errors in the Form

Now that we’ve added these additional validation rules, we need to ensure errors are displayed appropriately in the form. Here’s how you can display error messages for each field:

{errors.name && <p>{errors.name.message}</p>}
{errors.email && <p>{errors.email.message}</p>}
{errors.age && <p>{errors.age.message}</p>}

By integrating Zod with React Hook Form, you create a powerful validation system that can handle complex rules:

  • Regex validation allows you to enforce specific patterns (like letters only for names).
  • Refinement provides the flexibility to implement custom rules (like checking email domains and ensuring age is even).

This combination makes your forms not only more robust but also more user-friendly, ensuring that the data collected meets your requirements. Happy coding!

Trendingblogs
The Software Development Life Cycle (SDLC): A Complete Guide for Software Success image

The Software Development Life Cycle (SDLC): A Complete Guide for Software Success

Zustand: A Lightweight State Management Library for React image

Zustand: A Lightweight State Management Library for React

From Bugs to Beauty: Integrating UI/UX Testing into Software Quality Assurance image

From Bugs to Beauty: Integrating UI/UX Testing into Software Quality Assurance

Why is a Gaming PC the Ultimate Tool for Both Gaming and Professional Development? image

Why is a Gaming PC the Ultimate Tool for Both Gaming and Professional Development?

Why React Native is the Best Choice for Cross-Platform Development image

Why React Native is the Best Choice for Cross-Platform Development

Let's Deep Dive Into Threads with Rust image

Let's Deep Dive Into Threads with Rust

Get the best of our content straight to your inbox!

Don’t worry, we don’t spam!

Frequently Asked Questions