Search code examples
javascriptreactjstypescriptreact-hook-formzod

Zod: at least one field should be valid


I'm creating a form where users can enter either their email or their phone, they can enter both, but they can't proceed without entering both. I'm using Zod and react-hook-form

This is my schema:

const emailValidator = z.string().email()

export const contactFirstStepSchema = z
    .object({
        email: z
            .string()
            .refine(v => (v ? emailValidator.safeParse(v).success : true), "Invalid email"),
        phone: z.string().refine(value => {
            if (value) return isValidPhoneNumber(value, 'FR')

            return true
        }, "Invalid number"),
        description: z.string().optional()
    })
    .partial()
    .refine(({ email, phone }) => (email === undefined || email === '') && (phone === undefined || phone === ''), {
        message:
            "At least one field should be provided (or both)."
    })

However, when I submit the form without entering the both fields, it doesn't work, what should be done in this case?

thank you.


Solution

  • you are using refine function completely opposite. The return value of the refine callback must be false to throw an error. but in your code, you return true if both were undefined or an empty string.

    this is the code you can use:

    const emailValidator = z.string().email()
    
    export const contactFirstStepSchema = z
        .object({
            email: z
                .string()
                .refine(v => (v ? emailValidator.safeParse(v).success : true), "Invalid email"),
            phone: z.string().refine(value => {
                if (value) return isValidPhoneNumber(value, 'FR')
    
                return true
            }, "Invalid number"),
            description: z.string().optional()
        })
        .partial()
        .refine(({ email, phone }) => email || phone), { // edited line
            message:
                "At least one field should be provided (or both)."
        })
    

    in this edit, the return value will be false, only if both of them have falsy value (null, undefined, empty string, <=0). or true if at least one of them have a truly value