> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/colinhacks/zod/llms.txt
> Use this file to discover all available pages before exploring further.

# .transform()

> Transform data after validation with custom functions

## Overview

The `.transform()` method allows you to transform validated data into a different shape or type. Transformations run after validation succeeds, ensuring you always work with valid data.

## Signature

```typescript theme={null}
schema.transform((data, ctx) => transformedData)
```

<ParamField path="data" type="Output" required>
  The validated output data from the schema
</ParamField>

<ParamField path="ctx" type="RefinementCtx" required>
  Context object with `addIssue()` method for adding validation errors
</ParamField>

**Returns:** `ZodPipe<Schema, ZodTransform<NewOutput, Output>>`

## Basic Usage

### Simple Transformations

```typescript theme={null}
const stringToNumber = z.string().transform((val) => Number(val));

stringToNumber.parse("42"); // 42

type Input = z.input<typeof stringToNumber>;   // string
type Output = z.output<typeof stringToNumber>; // number
```

### String Transformations

```typescript theme={null}
const uppercase = z.string().transform((val) => val.toUpperCase());

uppercase.parse("hello"); // "HELLO"

const length = z.string().transform((val) => val.length);

length.parse("hello"); // 5
```

## Async Transformations

Transforms can be asynchronous:

```typescript theme={null}
const fetchUser = z.number().transform(async (id) => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
});

const user = await fetchUser.parseAsync(123);
```

<Warning>
  Async transforms must use `parseAsync()` or `safeParseAsync()`. Calling `parse()` with an async transform will throw an error.
</Warning>

```typescript theme={null}
const asyncSchema = z.number().transform(async (n) => String(n));

// ❌ This will throw
try {
  asyncSchema.parse(42);
} catch (error) {
  console.log("Cannot use parse() with async transforms");
}

// ✅ This works
await asyncSchema.parseAsync(42); // "42"
```

## Adding Validation During Transform

Use the context object to add validation errors:

```typescript theme={null}
const allowedStrings = ["foo", "bar", "baz"];

const schema = z.string().transform((data, ctx) => {
  if (!allowedStrings.includes(data)) {
    ctx.addIssue({
      code: "custom",
      message: `${data} is not an allowed value`,
    });
  }
  return data.length;
});

const result = schema.safeParse("invalid");

if (!result.success) {
  console.log(result.error.issues[0].message);
  // "invalid is not an allowed value"
}
```

### Using z.NEVER

Return `z.NEVER` to exclude a value from the output type:

```typescript theme={null}
const schema = z.number()
  .optional()
  .transform((val, ctx) => {
    if (!val) {
      ctx.addIssue({
        code: "custom",
        message: "Value is required",
      });
      return z.NEVER;
    }
    return val;
  });

type Output = z.output<typeof schema>; // number (not number | undefined)

const result = schema.safeParse(undefined);
// Error: "Value is required"
```

## Object Transformations

### Type Coercion in Objects

```typescript theme={null}
const schema = z.object({
  id: z.number().transform(String),
  createdAt: z.string().transform((s) => new Date(s)),
});

const result = schema.parse({
  id: 123,
  createdAt: "2024-01-01"
});

type Input = z.input<typeof schema>;
// { id: number; createdAt: string }

type Output = z.output<typeof schema>;
// { id: string; createdAt: Date }
```

### Reshaping Objects

```typescript theme={null}
const apiSchema = z.object({
  user_id: z.number(),
  user_name: z.string(),
  created_at: z.string(),
}).transform((data) => ({
  id: data.user_id,
  name: data.user_name,
  createdAt: new Date(data.created_at),
}));

const result = apiSchema.parse({
  user_id: 1,
  user_name: "Alice",
  created_at: "2024-01-01"
});
// { id: 1, name: "Alice", createdAt: Date }
```

## Chaining Transformations

You can chain multiple transformations:

```typescript theme={null}
const schema = z.string()
  .transform((s) => Number(s))
  .transform((n) => n * 2)
  .transform((n) => String(n));

schema.parse("5"); // "10"
```

## Combining with Refinements

### Refinement Before Transform

Validate before transforming:

```typescript theme={null}
const schema = z.string()
  .refine((s) => !isNaN(Number(s)), "Must be numeric")
  .transform(Number);

schema.parse("42");     // 42
schema.parse("hello");  // Error: "Must be numeric"
```

### Transform Then Refine

Transform, then validate the result:

```typescript theme={null}
const schema = z.string()
  .transform(Number)
  .refine((n) => n > 0, "Must be positive");

schema.parse("42");  // 42
schema.parse("-5");  // Error: "Must be positive"
```

## Short-Circuiting on Errors

By default, transforms don't run if validation fails:

```typescript theme={null}
const schema = z.string()
  .refine(() => false, "Validation failed")
  .transform((val) => {
    console.log("This never runs");
    return val.toUpperCase();
  });

const result = schema.safeParse("hello");
// Only validation error, transform is skipped
```

## Context Methods

### ctx.addIssue()

Add a validation error:

```typescript theme={null}
schema.transform((data, ctx) => {
  ctx.addIssue({
    code: "custom",
    message: "Custom error message",
    path: ["field"],  // Optional path
    fatal: true,       // Optional: stop validation
  });
  return data;
});
```

#### Short Form

Use a string for simple custom messages:

```typescript theme={null}
schema.transform((data, ctx) => {
  ctx.addIssue("Something went wrong");
  return data;
});
```

### Continuing After Errors

By default, adding an issue stops further validation. Set `continue: true` to continue:

```typescript theme={null}
const schema = z.string()
  .transform((val, ctx) => {
    ctx.addIssue({
      code: "custom",
      message: "Warning: non-critical issue",
      continue: true,  // Allow validation to continue
    });
    return val;
  })
  .refine(() => false, "Another check");

const result = schema.safeParse("test");
// Both errors are reported
```

## Common Patterns

### JSON Parsing

```typescript theme={null}
const jsonSchema = z.string().transform((str, ctx) => {
  try {
    return JSON.parse(str);
  } catch (e) {
    ctx.addIssue({
      code: "custom",
      message: "Invalid JSON",
    });
    return z.NEVER;
  }
});

const schema = jsonSchema.pipe(
  z.object({
    name: z.string(),
    age: z.number(),
  })
);
```

### Trimming and Normalizing

```typescript theme={null}
const normalizedString = z.string()
  .transform((s) => s.trim())
  .transform((s) => s.toLowerCase())
  .refine((s) => s.length > 0, "Cannot be empty after trimming");

normalizedString.parse("  HELLO  "); // "hello"
```

### Default Values with Transform

```typescript theme={null}
const withDefault = z.string()
  .optional()
  .transform((val) => val ?? "default");

withDefault.parse(undefined); // "default"
withDefault.parse("hello");   // "hello"
```

### Computing Derived Fields

```typescript theme={null}
const personSchema = z.object({
  firstName: z.string(),
  lastName: z.string(),
}).transform((data) => ({
  ...data,
  fullName: `${data.firstName} ${data.lastName}`,
}));

const result = personSchema.parse({
  firstName: "John",
  lastName: "Doe"
});
// { firstName: "John", lastName: "Doe", fullName: "John Doe" }
```

### Async Data Enrichment

```typescript theme={null}
const enrichedUser = z.object({
  id: z.number(),
  email: z.string().email(),
}).transform(async (user) => {
  const profile = await fetchProfile(user.id);
  const posts = await fetchPosts(user.id);
  
  return {
    ...user,
    profile,
    posts,
  };
});

const user = await enrichedUser.parseAsync({
  id: 1,
  email: "user@example.com"
});
```

## Encoding Errors

Transformations are unidirectional by default. Use `.encode()` will throw:

```typescript theme={null}
const schema = z.string().transform((val) => val.length);

try {
  z.encode(schema, 5);
} catch (error) {
  console.log(error.message);
  // "Encountered unidirectional transform during encode: ZodTransform"
}
```

For bidirectional transformations, use [z.codec()](/api/utilities/codec).

## Type Inference

Transforms affect type inference:

```typescript theme={null}
const schema = z.object({
  stringToNumber: z.string().transform(Number),
  numberToString: z.number().transform(String),
});

type Input = z.input<typeof schema>;
// { stringToNumber: string; numberToString: number }

type Output = z.output<typeof schema>;
// { stringToNumber: number; numberToString: string }
```

## Error Handling Examples

### Transform Validation Errors

```typescript theme={null}
const strs = ["foo", "bar"];

const schema = z.string().transform((data, ctx) => {
  const i = strs.indexOf(data);
  if (i === -1) {
    ctx.addIssue({
      input: data,
      code: "custom",
      message: `${data} is not one of our allowed strings`,
    });
  }
  return data.length;
});

const result = schema.safeParse("asdf");

if (!result.success) {
  console.log(result.error.issues);
  // [
  //   {
  //     code: "custom",
  //     message: "asdf is not one of our allowed strings",
  //     path: []
  //   }
  // ]
}
```

## Best Practices

<Card title="Transform after validation">
  Always validate your data before transforming it. Transformations assume valid input.
</Card>

<Card title="Use meaningful transformations">
  Transformations should have a clear purpose. Don't use them for side effects.
</Card>

<Card title="Handle errors gracefully">
  Use `ctx.addIssue()` for expected error cases. Let unexpected errors throw naturally.
</Card>

<Card title="Consider performance">
  Async transforms and complex transformations can impact performance. Cache results when possible.
</Card>

<Card title="Type safety matters">
  Let TypeScript infer types when possible. Explicit type annotations can catch transformation errors.
</Card>

## See Also

* [Pipe](/api/utilities/pipe) - Chain schemas together
* [Refine](/api/utilities/refine) - Custom validation
* [Coerce](/api/utilities/coerce) - Type coercion
* [Codec](/api/utilities/codec) - Bidirectional transformations
