# How to Use Zod's Record for Key-Value Validation: Syntax, Examples, and Source Code

> Master Zod's record for key-value validation. Learn syntax and see examples to ensure your dictionary objects meet specific key and value schema requirements. Read now.

- Repository: [Colin McDonnell/zod](https://github.com/colinhacks/zod)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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](https://github.com/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`](https://github.com/colinhacks/zod/blob/main/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`](https://github.com/colinhacks/zod/blob/main/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:

1. Validates the property key against `keyType`
2. Validates the property value against `valueType`
3. 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`](https://github.com/colinhacks/zod/blob/main/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`](https://github.com/colinhacks/zod/blob/main/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

```typescript
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

```typescript
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)

```typescript
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

```typescript
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

```typescript
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

```typescript
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`](https://github.com/colinhacks/zod/blob/main/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 `additionalProperties` mappings 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`](https://github.com/colinhacks/zod/blob/main/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`](https://github.com/colinhacks/zod/blob/main/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.