Skip to main content

Custom/Conditional Validation

In your form schema, use .refine and .superRefine for field validation.

Validating a Single Field

.refine is convenient for single-field validation:

const schema = z.object({
serverUrl: z
.string()
.url()
.optional()
.refine((url) => {
try {
return url == null || new URL(url).protocol === 'mqtts'
} catch {
return false
}
}, 'must be a valid mqtts url'),
})

error message example

Comparing Fields

To validate one field against another, use conditionalValidate. It is similar to .superRefine, but ensures that the refinements are checked even if unrelated fields failed to parse.

import { conditionalValidate } from '@jcoreio/zod-forms'

const schema = conditionalValidate(
z.object({
foo: z.string(),
min: z.number().finite(),
max: z.number().finite(),
})
).conditionalRefine(
// Pick the fields the refinement depends on here
(s) => s.pick({ min: true, max: true }),
// This refinement will only be checked if min and max are successfully parsed
({ min, max }) => min <= max,
[
{ path: ['min'], message: 'must be <= max' },
{ path: ['max'], message: 'must be >= min' },
]
)

error message example

Conditional Validation

To do conditional validation, use conditionalValidate. It is similar to .superRefine, but ensures that the refinements are checked even if unrelated fields failed to parse.

import { conditionalValidate } from '@jcoreio/zod-forms'

const schema = conditionalValidate(
z.object({
dataType: z.enum(['string', 'number']),
displayPrecision: z.number().finite().optional(),
})
).conditionalRefine(
(s) => s.pick({ dataType: true, displayPrecision: true }),
({ dataType, displayPrecision }) =>
dataType !== 'number' || displayPrecision != null,
{ path: ['displayPrecision'], message: 'Required when dataType is number' }
)

error message example inactive validation example