0

Using react-hook-form, and zod within a Next.js project. Trying to get .optional() to work. It works with simple strings. See the schema below.

//works

const FormSchema = z.object({
    website: z.string().optional()
});

But it does not work when I add the .url() flag. It only checks for the valid url. If the field is blank, it throws an error. In other words, the field is no longer optional. It accepts a valid url, but not a blank input field. Of course I want it to accept a blank input and a valid url as the only valid inputs.

//not working. the field is no longer optional.

const FormSchema = z.object({
    website: z.string().url().optional()
})

Perhaps the problem has to do with the input field returning the wrong data-type when it's blank?

<label className="block">
       <span className="block text-white">Website</span>
       <input
              id="email-input"
              type="text"
              className={`block border text-lg px-4 py-3 mt-2  border-gray-200 focus:bg-white text-gray-900 focus:ring-purpleLight focus:ring-2 outline-none w-full  disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed`}
              {...register("website")}
              disabled={isSubmitting}
              placeholder="Website"
              value={undefined}
         />
     </label>
{errors.website && (
    <p className="mt-1 text-sm text-red-600">
    {errors.website.message}
    </p>
)}
OWolf
  • 5,012
  • 15
  • 58
  • 93

1 Answers1

1

Perhaps the problem has to do with the input field returning the wrong data-type when it's blank?

I think this is likely your problem, although I'm not familiar with what react-hook-form might be doing to transform the input before handing it to your schema. The empty field will have the empty string '' as a value when it's blank. If you just use zod directly:

z.string().optional().parse(''); // succeeds because '' is a string
// Whereas this will fail because although '' is a string, it does not
// match the url pattern.
z.string().url().optional().parse('');

.optional() allows for the input value to be undefined so for example:

// These will both successfully parse because of `optional`
z.string().optional().parse(undefined);
z.string().url().optional().parse(undefined);

Going out slightly on a limb here, but if you wanted '' to pass you could add a preprocess step where you convert '' to undefined:

const s = z.preprocess(
  (arg) => (arg === "" ? undefined : arg),
  z.string().url().optional()
);

console.log(s.safeParse("")); // success (data: undefined)
console.log(s.safeParse("test")); // failure (not a url)
console.log(s.safeParse(undefined)); // success (data: undefined)
Souperman
  • 5,057
  • 1
  • 14
  • 39