How to Use Zod's Record for Key-Value Validation: Syntax, Examples, and Source Code
Use z.record(keySchema, valueSchema) to validate dictionary objects where every key must match the key schema and every value must match the value schema.
Zod's record schema provides a type-safe way to validate dynamic key-value objects (dictionaries) in TypeScript. As implemented in the colinhacks/zod repository, this utility creates a ZodRecord instance that enforces strict validation on both property keys and values. Understanding how to use Zod's record for key-value validation ensures runtime type safety for configuration objects, lookup tables, and arbitrary property mappings.
What Is a Zod Record Schema?
A ZodRecord represents a dictionary type where both keys and values undergo schema validation. According to the source code in packages/zod/src/v4/classic/schemas.ts (lines 1490–1517), the record() builder function returns a new instance with type: "record" and stores the supplied keyType and valueType definitions.
The interface definition (lines 1491–1505) establishes that a ZodRecord maintains two core properties:
keyType: Constrains which property names are valid (must be strings, numbers, symbols, or enums resolving to these primitives)valueType: Validates the data associated with each key (accepts any Zod schema)
Supported Key and Value Types
Zod enforces specific constraints on record keys to ensure JavaScript object compatibility. Valid key schemas include:
- Primitive schemas:
z.string(),z.number(),z.symbol() - Enum schemas:
z.enum(["a", "b"]) - Literal schemas:
z.literal("specificKey") - Union schemas combining the above
For value schemas, any Zod type works—including objects, unions, arrays, or nested records.
Internal Validation Algorithm
The validation logic resides in packages/zod/src/v4/core/schemas.ts (around lines 2762–2802). The algorithm iterates over every enumerable property of the input object using a for...in loop:
- Validates the property key against
keyType - Validates the property value against
valueType - Collects errors for both key and value violations
If either check fails, Zod throws a ZodError specifying whether the invalid data was a key or value.
Record Variants: Strict, Partial, and Loose
Zod provides three modes for record validation, controlled through helper functions or the mode parameter:
Strict Mode (Default)
Only keys explicitly allowed by the key schema are permitted. Use z.record(keyType, valueSchema) or pass { mode: "strict" }.
Partial Record
Makes all keys optional (the record can be empty). Implemented via z.partialRecord(keyType, valueSchema) in packages/zod/src/v4/classic/schemas.ts.
Loose Record
Allows additional keys beyond those specified by the key schema to pass through unchanged. Use z.looseRecord(keyType, valueSchema) or { mode: "loose" }.
JSON Schema and OpenAPI Integration
When generating JSON Schema definitions, the recordProcessor in packages/zod/src/v4/core/json-schema-processors.ts (lines 430–449) converts ZodRecord instances into standard JSON Schema objects. It sets additionalProperties to the value schema's JSON representation and derives propertyNames constraints from the key schema. This enables seamless OpenAPI documentation generation for dynamic dictionary endpoints.
Practical Code Examples
1. Basic String-to-String Dictionary
import { z } from "zod";
const StringDict = z.record(z.string(), z.string());
StringDict.parse({ a: "hello", b: "world" }); // ✅ passes
StringDict.parse({ a: "hello", 42: "nope" });
// ❌ ZodError: Invalid key in record (key must be a string)
2. Enum Keys with Number Values
enum Fish {
Tuna = "tuna",
Salmon = "salmon",
}
const FishPrices = z.record(z.enum([Fish.Tuna, Fish.Salmon]), z.number());
FishPrices.parse({ tuna: 10, salmon: 12 }); // ✅
FishPrices.parse({ tuna: 10, cod: 5 });
// ❌ ZodError: Invalid key in record (key not in enum)
3. Mixed Literal Keys (String, Number, Symbol)
const MixedKey = z.union([
z.literal("id"),
z.literal(0),
z.literal(Symbol.for("meta")),
]);
const MixedRecord = z.record(MixedKey, z.boolean());
MixedRecord.parse({ id: true, 0: false, [Symbol.for("meta")]: true }); // ✅
4. Partial Records with Optional Keys
const PartialUser = z.partialRecord(z.enum(["name", "age"]), z.string());
PartialUser.parse({}); // ✅ – all keys optional
PartialUser.parse({ name: "Bob" }); // ✅
5. Loose Records Allowing Extra Keys
const Loose = z.looseRecord(z.string(), z.number());
Loose.parse({ a: 1, b: 2, extra: 99 }); // ✅ extra key passes through
6. Nested Records in Object Schemas
const User = z.object({
id: z.string(),
preferences: z.record(z.string(), z.union([z.string(), z.number()])),
});
User.parse({
id: "u1",
preferences: { theme: "dark", fontsize: 14 },
}); // ✅
Summary
- Use
z.record(keyType, valueType)to validate dictionaries where both keys and values require type checking according to the colinhacks/zod source code. - Valid key schemas include strings, numbers, symbols, enums, literals, and unions of these types.
- The validation engine iterates through all enumerable properties in
packages/zod/src/v4/core/schemas.ts, checking keys before values. - Choose between strict (default), partial (optional keys), and loose (extra keys allowed) modes based on your data requirements.
- Records automatically convert to proper JSON Schema with
additionalPropertiesmappings for API documentation.
Frequently Asked Questions
What key types can I use with Zod record?
Zod record supports string, number, and symbol primitives, as well as enum, literal, and union schemas that resolve to these types. Complex objects or arrays cannot serve as record keys due to JavaScript object property constraints.
How do I make record keys optional?
Use z.partialRecord(keySchema, valueSchema) to create a record where no keys are required. This helper, defined in packages/zod/src/v4/classic/schemas.ts, produces a schema that accepts empty objects or any subset of the specified keys.
What's the difference between strict and loose record modes?
Strict mode (the default) rejects any keys that don't match the key schema exactly. Loose mode, accessed via z.looseRecord() or { mode: "loose" }, allows additional keys to pass through validation unchanged while still validating values for keys that match the schema.
How does Zod convert records to JSON Schema?
The recordProcessor in packages/zod/src/v4/core/json-schema-processors.ts (lines 430–449) maps records to JSON Schema by setting additionalProperties to the value schema's JSON representation and constraining propertyNames based on the key schema. This ensures valid OpenAPI specifications for dynamic key-value endpoints.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →