Arrays
Lists of things
APIs deal with lists all the time. A list of tags on a blog post. A list of items in an order. A list of email addresses to notify. We know how to validate individual values and objects, but what about a collection of them?
z.array() wraps any schema to validate an array where every element matches that schema.
Basic arrays
import { z } from "zod/v4";
z.array(z.string()); // array of strings
z.array(z.number()); // array of numbers
z.array(z.string().email()); // array of valid emails Let’s see what happens with different inputs:
z.array(z.string()).parse(["a", "b", "c"]); // returns ["a", "b", "c"]
z.array(z.string()).parse(["a", 42, "c"]); // fails: element at index 1 is not a string
z.array(z.string()).parse("not an array"); // fails: "Expected array, received string"
z.array(z.string()).parse([]); // passes: empty array is valid Notice that an empty array passes by default. Sometimes that is fine (a post with no tags). Sometimes it is not (an order with no items). Constraints let you control this.
Array constraints
z.array(z.string()).min(1); // at least 1 element (not empty)
z.array(z.string()).max(10); // at most 10 elements
z.array(z.string()).length(3); // exactly 3 elements
z.array(z.string()).nonempty(); // same as min(1) — at least 1 element z.array(z.string()).nonempty().parse([]); // fails: "Array must contain at least 1 element(s)"
z.array(z.string()).nonempty().parse(["hello"]); // passes
z.array(z.string()).max(2).parse(["a", "b", "c"]); // fails: "Array must contain at most 2 element(s)" .nonempty() and .min(1) do the same thing. Use whichever reads better to you.
Arrays of objects
This is the most common pattern in API development: an array of structured items where each item has to be valid.
const TagSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1),
});
const BookSchema = z.object({
title: z.string().min(1),
tags: z.array(TagSchema).min(1).max(10), // 1-10 tags, each validated
}); Every element in the tags array gets validated against TagSchema. If any element fails, the error path includes the array index so you know exactly which item has the problem:
BookSchema.parse({
title: "Kindred",
tags: [
{ id: "550e8400-...", name: "sci-fi" },
{ id: "not-a-uuid", name: "fiction" }, // invalid id
],
});
// Error path: ["tags", 1, "id"] — element at index 1, field "id" The path ["tags", 1, "id"] tells you: the tags array, at index 1, the id field is invalid. A frontend could highlight exactly the right input in a dynamic list of tags. This is how production apps surface validation errors on repeated form fields.
Nested arrays
Arrays can contain arrays, if your data has that shape:
const MatrixSchema = z.array(z.array(z.number()));
MatrixSchema.parse([
[1, 2],
[3, 4],
]); // passes
MatrixSchema.parse([[1, "2"]]); // fails: "Expected number, received string" at [0][1] You probably will not need this often, but it is good to know the nesting works at any depth.
Applying to the contact form
We could extend our contact form to support optional tags:
const ContactSchema = z.object({
name: z.string().trim().min(1).max(100),
email: z.string().trim().toLowerCase().email(),
phone: z.string().trim().min(7).max(20).optional(),
subject: z.enum(["general", "support", "sales", "feedback"]),
message: z.string().trim().min(10).max(5000),
tags: z.array(z.string().min(1)).max(5).optional(), // up to 5 tags, optional
}); The tags field is optional (you do not have to send it), but if you do, it must be an array of non-empty strings with a maximum of 5 elements. Each element is individually validated.
Next up: unions and discriminated unions. Sometimes a value can be one of several different shapes, and you need Zod to figure out which one it is.
Exercises
Exercise 1: Create a schema for an array of emails. Parse ["[email protected]", "invalid"]. Check the error path for the invalid element.
Exercise 2: Create a schema for an array of objects (items in an order). Validate that each item has a productId and quantity.
Exercise 3: Use .nonempty() on an array. Parse an empty array and verify it fails.
How does Zod report errors for invalid elements in an array?