RepoPilot

How do I define a custom validator in zod?

About colinhacks/zod · 2 messages · last activity 7/26/2026

Question

How do I define a custom validator in zod?

RepoPilot

Zod provides the refine() method to define custom validators. Here's how it works:

Basic Refine Usage

The refine() method accepts a validation function and optional error message [packages/zod/src/v3/types.ts:L318-L357]:

refine(
  check: (arg: Output) => unknown,
  message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)
): ZodEffects<this, Output, Input>

The validation function receives the parsed value and should return a truthy value if valid, or falsy if invalid. If the check fails, an error with code ZodIssueCode.custom is added to the context.

Type-Narrowing Refine

You can also use refine() with a type guard to narrow the output type:

refine(
  check: (arg: Output) => arg is RefinedOutput,
  message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)
): ZodEffects<this, RefinedOutput, Input>

Error Messages

The message parameter is flexible — you can provide:

  • A string message
  • A function that receives the value and returns custom error properties
  • A CustomErrorParams object directly

The getIssueProperties() helper in the implementation [packages/zod/src/v3/types.ts:L334-L339] handles all three cases.

For built-in validators, Zod also uses a check() system with specific validation kinds like "email", "regex", "startsWith", "endsWith", etc. [packages/zod/src/v3/types.ts:L891-L930], but refine() is the primary API for custom validation logic.

FOLLOW_UPS:

  • How do I create async custom validators in zod?
  • What are the different check kinds for string validation?
  • Can I combine multiple refinements on one schema?

Want to ask your own question?

Open chat for colinhacks/zod

Public Q&A. Generated by RepoPilot from the actual source of colinhacks/zod. AI answers can be incomplete or stale — verify before relying on them.