Zod Arrays: From Basics to Array of Objects Validation


A Zod array schema allows you to validate arrays in TypeScript, ensuring each element meets specific type and structural rules.

In real-world applications, arrays often come from user input or external APIs and can contain unexpected or invalid data.

Zod arrays help ensure each element has the correct type and structure, preventing runtime errors, maintaining data integrity, and reducing the need for repetitive manual checks.

Try It Yourself: Code Example

import { z } from 'zod';

const numbers = z.array(z.number());

The above example defines a Zod schema that validates an array of numbers. Each element in the array must be a number, and the entire array will fail validation if any item doesn’t meet that requirement.

Error Handling with Zod Array

Handling errors in Zod arrays ensures that invalid or unexpected array data is caught and managed gracefully. By using Zod’s parsing methods, developers can detect type mismatches, empty arrays, or other structural issues, providing meaningful feedback and preventing runtime failures.

import { z } from "zod";

const schema = z.array(z.string()).min(1, "Array cannot be empty");

const input = [123, "orange"]; // Invalid example

const result = schema.safeParse(input);
if (!result.success) console.error(result.error.errors);
else console.log(result.data);

Explanation:

  • z.array(z.string()) ensures all elements are strings.
  • .min(1, "Array cannot be empty") prevents empty arrays.
  • safeParse returns errors without throwing, so you can handle them gracefully.

Zod Arrays by Element Type

This section explores how to define and validate Zod arrays based on their element types. You’ll learn how to handle arrays of strings, numbers, objects, enums, and more, with guidance on type safety, and validation rules.

Working with Primitive Types in Zod Arrays

Arrays of strings can be validated in Zod using z.array(z.string()), which ensures each element is a string. For example, z.array(z.string()) will accept ["apple", "banana"] but fail for ["apple", 42].

Arrays of numbers can be validated using z.array(z.number()), which ensures every element is a number. For example, z.array(z.number()) will accept [1, 2, 3] but fail for [1, "2", 3].

Arrays of booleans can be validated with z.array(z.boolean()), ensuring each element is true or false. For example, z.array(z.boolean()) accepts [true, false, true] but rejects [true, 0, false].

Arrays of BigInts can be validated using z.array(z.bigint()), which ensures each element is a BigInt. For example, [1n, 2n] passes, but [1n, 2] fails.

Working with Complex Types in Zod Arrays

Arrays of objects can be validated with z.array(z.object({ name: z.string(), age: z.number() })), ensuring each element matches the defined object schema. For example, [ {name: "Alice", age: 25} ] passes, while [ {name: "Bob"} ] fails.

Arrays of interfaces can be validated with z.array(z.object(interfaceSchema)), ensuring each object conforms to the interface.

Arrays of enums can be validated with z.array(z.enum(["Admin","User","Guest"])), ensuring each element is one of the allowed enum values.

Arrays of literals can be validated with z.array(z.literal("fixedValue")), ensuring all elements exactly match the literal.

Arrays of files or images can be validated with z.array(z.instanceof(File)), ensuring each element is a File instance.

Arrays of checkboxes can be validated with z.array(z.boolean()), representing true/false selections.

Nested arrays can be validated with z.array(z.array(z.string())), ensuring each inner array conforms to its schema.

Arrays with multiple types can be validated with z.array(z.union([z.string(), z.number()])), allowing heterogeneous elements like [1, "two", 3] while rejecting [true, "two"].


Applying Constraints to Arrays: Min, Max, Length, and Non-Empty in Zod

When working with arrays in Zod, you often need to enforce how many elements an array can contain. You can restrict an array to a maximum number of items using .max() or ensure it has a minimum number of items with .min(). For arrays that must have an exact number of elements, combining .min() and .max() with the same value works perfectly.

Array Not Empty

To ensure an array is not empty in Zod, use .nonempty(). This guarantees that at least one item exists in the array at runtime.

z.array(z.string()).nonempty();

For full examples and edge cases of arrays using .min(), .max(), and .length(), check out Edge Cases of Arrays with Length Constraints.


Transforming and Refining Arrays: Using .transform(), .pipe(), and .refine()

Zod goes beyond static validation by offering tools to transform and refine array data dynamically. You can sanitize, compute, or reshape array inputs before or after validation using methods like .transform(), .preprocess(), .pipe(), and .refine().

This section explores how to enhance array validation logic with transformation pipelines and custom rules using real-world use cases. These are the possible Zod array variations of transform, preprocess and refinement:

Transform Array to String

Use .transform() to convert an array into a string — often joined by a delimiter like a comma.

z.array(z.string()).transform(arr => arr.join(','))

Explore more scenarios where .transform() is applied to arrays in this Zod Transform guide.

Preprocess Array Example

Use z.preprocess() to transform incoming data (e.g. strings, JSON, or raw values) before it reaches the array schema. This is useful when data isn’t in array form yet but should be.

z.preprocess(
  (val) => (typeof val === 'string' ? val.split(',') : val),
  z.array(z.string())
);

Array Refine Path Example

Use .refine() on an array schema to apply custom validation logic, and include the path option to associate errors with specific parts of the array.

z.array(z.string()).refine(
  (arr) => arr.includes("admin"),
  {
    message: "At least one value must be 'admin'",
    path: ["0"], // optional: points to specific index/key
  }
);

Making Arrays Optional, Nullable, or Default: Flexible Validation in Zod

Not all data is always present, and Zod supports flexible handling for optional, nullable, and defaultable arrays. You can allow undefined, null, or supply fallback values seamlessly using .optional(), .nullable(), .default(), or .catch().

In this section, we’ll walk through how to create resilient schemas for optional or missing array inputs, along with scenarios where each method shines.

Array of Objects Optional

You can define an optional array of objects in Zod by combining .array() with .optional(). This is useful when the array may or may not be present in the input, such as optional form sections or API fields.

const itemSchema = z.object({
  name: z.string(),
  price: z.number(),
});
z.array(itemSchema).optional()

Real World Examples: Shopping cart items, User addresses, Survey responses.

Array Nullable

Zod allows arrays to be explicitly nullable using .nullable(), meaning the value can either be an array or null. This is useful for optional fields that may be reset or intentionally cleared in forms or APIs.

z.array(z.string()).nullable()

Real World Examples: Blog post tags, Delivery instructions, Product add-ons

Array Empty Default Value

Use .default() with z.array() to provide a fallback when the field is missing or undefined. This is helpful in forms or APIs where the array should always exist, even if empty.

z.array(z.string()).default([])

Real World Examples: Cart items, Empty tags list, Uploaded files

Array Catch

The .catch() method in Zod sets a fallback value when parsing fails — useful for replacing invalid or malformed array inputs with a safe default.

z.array(z.string()).catch([])

Real World Examples: Applied coupons, Uploaded images


Summary

Zod’s array schema capabilities give you granular control over data structure, content, and behavior. From simple arrays of strings or numbers to complex nested objects with custom transformations and refinements, Zod ensures your array data is both valid and type-safe.

Whether you’re enforcing constraints like minimum length or fixed size, transforming arrays into other formats, or gracefully handling optional, nullable, or invalid inputs, Zod provides a declarative and expressive API to cover every use case. By applying these techniques, you can confidently validate real-world data scenarios such as form submissions, API payloads, user inputs, and application state.

By mastering Zod arrays, you’re building more predictable and safer applications — with minimal runtime surprises and maximum developer clarity.

Author

Sharukhan is the founder of Tecktol. He has worked as a software engineer specializing in full-stack web development.