Custom business validation
Enforce a domain rule that has no direct built-in Zod check.
Zod · Module
.refine() adds custom validation to any Zod schema. Its predicate passes on truthy results and reports a custom issue on falsy results, with optional error and continuation settings.
Call .refine on a schema when validation needs a custom predicate beyond Zod's built-in checks. A truthy predicate result accepts the value; a falsy result adds a custom validation issue, optionally configured with an error message, path, or abort behavior.
.refine(validator: (data: T) => any, params?: RefineParams)| Name | Type | Description |
|---|---|---|
validator | (data: T) => any | A predicate receiving the value inferred by the schema; truthy results pass and falsy results fail. |
params | RefineParams | Optional refinement configuration such as an error message, error path, parameters, or abort behavior. |
A new schema containing the refinement; parsing succeeds only when the predicate returns a truthy value.
import { z } from "zod";
const username = z.string()
.min(3)
.refine((value) => value === value.toLowerCase(), {
error: "Must be lowercase",
abort: true,
});
console.log(username.safeParse("alice").success); // true
console.log(username.safeParse("Al").success); // falseThe schema first applies the built-in length check, then uses refine for a custom lowercase requirement.
Enforce a domain rule that has no direct built-in Zod check.
Attach a targeted message or location to a failed custom check.
Stop dependent checks after an earlier refinement fails.
refine attaches custom validation to an existing schema. Its predicate receives the schema's inferred value, and truthy results pass while falsy results produce a validation issue.
Follow-up: How would you report several distinct failures? Use superRefine, which can create multiple issues and use Zod's internal issue types.
The second argument configures the failure, including an error message and an optional path appended to the error location. Current Zod also supports abort to stop validation after that check fails.
Follow-up: When is abort useful? Use it when subsequent checks depend on the refinement or should not execute after failure.
refine is intended for a single custom validation issue. superRefine is the more capable alternative when validation must emit multiple issues or customize their issue codes.
Follow-up: Does a predicate need to return boolean? No; any truthy value passes and any falsy value fails.
superRefineerrorabortpath