Skip to main content

Overview

Transformations allow you to modify data after validation. The .transform() method runs after all validations pass, converting the data to a different shape or type.

Basic Transformation

Use .transform() to modify validated data:
From packages/zod/src/v4/classic/tests/transform.test.ts:86-91:

Transformation Signature

From packages/zod/src/v4/classic/schemas.ts:114-116, the .transform() method signature:
Transformations receive two parameters:
  1. arg - The validated output value
  2. ctx - A context object for adding issues

Common Use Cases

Type Coercion

Convert strings to numbers:
From packages/zod/src/v4/classic/tests/transform.test.ts:94-102:

String Manipulation

Data Normalization

Object Reshaping

Async Transformations

Transformations can be asynchronous:
From packages/zod/src/v4/classic/tests/transform.test.ts:104-113:
Async transformations require using parseAsync() or safeParseAsync(). Calling parse() on a schema with async transformations will throw an error.

Chaining Transformations

Multiple transformations can be chained:
From packages/zod/src/v4/classic/tests/transform.test.ts:186-192:

Error Handling in Transformations

Use the context object to add validation errors:
From packages/zod/src/v4/classic/tests/transform.test.ts:4-28:

Using z.NEVER

From packages/zod/src/v4/classic/tests/transform.test.ts:62-83:
Returning z.NEVER from a transformation signals that validation should fail, and it also narrows the output type appropriately.

Input vs Output Types

Transformations change the output type while keeping the input type:

Practical Examples

Date Parsing

JSON Parsing

URL Slug Generation

Form Data Processing

API Response Transformation

Transformation Order

Transformations run AFTER all validations:
From packages/zod/src/v4/classic/tests/transform.test.ts:194-200:
If validation fails, transformations are never executed. This ensures you only transform valid data.

Transformations vs Defaults

Understand the difference:

Transformations vs Refinements

  • Refinements (.refine()) - Add validation, don’t change data
  • Transformations (.transform()) - Modify data after validation

Performance Considerations

Transformations add overhead to parsing. For high-performance scenarios, consider whether you really need to transform during validation or if it’s better to transform separately.

Combining with Pipes

Transformations can be combined with pipes for complex data flows:

Best Practices

  1. Keep transformations simple - Complex logic is hard to debug
  2. Use transformations for data coercion - Converting types, normalizing formats
  3. Handle errors explicitly - Use ctx.addIssue() for validation failures
  4. Consider performance - Avoid expensive operations in transforms
  5. Type safety - TypeScript will infer the output type correctly

Common Gotchas

Async in Sync Context

Returning Undefined

Next Steps