Custom Validation Integration
Cross-field, async, and conditional validation patterns using the real Zod and RHF APIs.
Prism doesn't define its own validation DSL, custom validation is ordinary Zod or ordinary React Hook Form, both fully available through the adapters described in React Hook Form Adapter and Zod Adapter.
Cross-field validation with .refine()
const schema = z
.object({
password: z.string().min(8),
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: 'Passwords must match',
path: ['confirmPassword'],
});path determines which field's field.error the message surfaces through.
Multiple independent checks with .superRefine()
const schema = z.object({ age: z.number() }).superRefine((data, ctx) => {
if (data.age < 18) {
ctx.addIssue({ code: 'custom', message: 'Must be at least 18', path: ['age'] });
}
if (data.age > 120) {
ctx.addIssue({ code: 'custom', message: 'Enter a realistic age', path: ['age'] });
}
});Async validation
A Zod schema can validate asynchronously (.refine(async (value) => ..., message)), and
createZodResolver adapts it the same way as a synchronous schema, @hookform/resolvers/zod
handles both. Submission waits for the resolver to resolve before handleSubmit's success or
failure callback runs, exactly as it would with plain React Hook Form and
@hookform/resolvers/zod.
Conditional validation (native RHF)
For validation that depends on something outside the Zod schema's own shape, for example a
feature flag or a value from form.watch(), use RHF's native per-field rules through
PrismField:
<PrismField
form={form}
name="promoCode"
rules={{
validate: (value) => (requiresPromoCode ? Boolean(value) || 'Promo code required' : true),
}}
/>See Combining RHF and Zod for how this interacts with a whole-form resolver on the same form.