5

I want to make a form with React-hook-form and zod resolver where all fields are optional but fields still required despite making them optional in zod schema:

const schema = z.object({
    name: z.string().min(3).max(50).optional(),
    description: z.string().min(3).max(255).optional(),
    image: clientImageSchema.optional(),
  })
const {...} = useForm({ resolver: zodResolver(schema) }) 

when submitting the form with blank inputs it validates fields as required. Where is the error or the mistake ?

5 Answers 5

9

As it says in the documentation, you must make a union between your schema and z.literal(""), so it doesn't trigger any error if the value is an empty string. See example.

const optionalUrl = z.union([z.string().url().nullish(), z.literal("")]);
Sign up to request clarification or add additional context in comments.

Comments

6

I had the same issue. There may be some bug or issue regarding the handling of empty strings. There was similar thing happening with enums https://github.com/react-hook-form/react-hook-form/issues/3985.

For now just running the field with .preprocess() seems to be working. What I mean is:

const schema = z.object({
  // Watch out, z.preprocess takes two arguments
  foo: z.preprocess(
    (foo) => {
      // this line won't work
      // return foo

      // this line will work, dunno why
      // console.log(typeof email)

      // I'm using this
      if (!foo || typeof foo !== 'string') return undefined
      return foo === '' ? undefined : foo
    },
    z
      .string()
      .email({
        message: 'Please correct your email address',
      })
      .optional(),
  ),
})

So this will give us an optional field that will be validated as an email field once not-empty. There may be some other way but that's what I've found.

Comments

1

The workaround I've found is to pass defaultValues to useForm

const defaultValues = {
  name: '',
  /** rest */
}

  useForm<ToolCreateApiData>({
    resolver: zodResolver(schema),
    defaultValues,
  });

1 Comment

In my case, I fixed adding undefined as the default value
0

I found this solution.

But it doesen't clear error message when clear field after failed submit.

Also you should handle 'undefined' fields in submit to not include it in fetch request.

const { handleSubmit, setValue, getValues, reset} = useForm<DataType>({
    resolver: zodResolver(DataSchema),
});

const preSubmit = () => {
    const values = getValues();
    if (values.name === "") setValue("name", undefined);
};

const submitForm = (data: DataType) => {
    console.log(data);
    reset();
};

<form onSubmit={(e) => {
    preSubmit();
    void handleSubmit(submitForm)(e);
}}>
    //...form fields
</form>

Comments

0

on your useForm default value, put empty string there to make it required even though it is mark as optional in zod schema, else put undefined.

in my example below it is an update user form where password is mark as optiona.

const form = useForm<z.infer<typeof updateUserSchema>>({
    resolver: zodResolver(updateUserSchema),
    reValidateMode : "onChange",
    defaultValues: {
        username: Data.User?.username ?? "", 
        email: Data.User?.email ?? "",
        password: undefined
    },
});

I marked it as undefined instead of empty string " " as it's getting required when submitted. So maybe that is what you want? Just place empty string on default value to make it required even if it is optional.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.