How do I define a custom validator in zod?
How do I define a custom validator in zod?
About colinhacks/zod · 2 messages · last activity 7/26/2026
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/zodPublic Q&A. Generated by RepoPilot from the actual source of colinhacks/zod. AI answers can be incomplete or stale — verify before relying on them.