Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

zod schema not triggering required_error for empty strings

I'm using the zod library to validate a form in my Next.js application. I've defined a schema for a subscription form that should validate an email field. I want to display a custom error message - Email is required - when the email field is left empty, but for some reason, the required_error property isn't working as expected. If I click on Subscribe without entering any email, I get the error message Email is invalid, instead of the error message Email is required.

enter image description here

Here's my schema:

export const subscribeSchema = z.object({
  email: z
    .string({ required_error: "Email is required" })
    .email({ message: "Email is invalid" }),
});

Here's is how I am using the schema in the Subscribe form:

"use client";

import * as React from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { useRouter } from "next/navigation";

import { cn } from "@/lib/util";
import { subscribeSchema } from "@/lib/validations/auth";
import { buttonVariants } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Icons } from "@/components/icons";
import { subscribeUser } from "@/lib/subscribe-user";

export type FormData = z.infer<typeof subscribeSchema>;

export function SubscribeForm() {
  const {
    register,
    handleSubmit,
    reset,
    formState: { errors },
  } = useForm<FormData>({ resolver: zodResolver(subscribeSchema) });

  const [isLoading, setIsLoading] = React.useState<boolean>(false);
  const [error, setError] = React.useState<string | null>(null);

  const router = useRouter();

  async function onSubmit(submittedData: FormData) {
    await subscribeUser({
      submittedData,
      setError,
      router,
      setIsLoading,
      reset,
    });
  }

  return (
    <div className="grid gap-6 rounded-md border border-neutral-200 bg-neutral-100 p-6">
      <div className="flex flex-col space-y-1">
        <h1 className="text-2xl">Subscribe</h1>
        <p>Get notified of the course launch.</p>
      </div>
      {error && (
        <p className="text-sm text-red-500 animate-in fade-in-0 slide-in-from-left-1">
          {error}
        </p>
      )}
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="grid gap-6">
          <div className="grid gap-1">
            <Label htmlFor="email">Email</Label>
            <Input
              className="bg-white"
              id="email"
              type="email"
              placeholder="[email protected]"
              autoCapitalize="none"
              autoComplete="email"
              autoCorrect="off"
              disabled={isLoading}
              {...register("email")}
            />
            {errors.email && (
              <p className="px-1 text-xs text-red-600 animate-in fade-in-0 slide-in-from-left-1">
                {errors.email.message}
              </p>
            )}
          </div>
          <button className={cn(buttonVariants())} disabled={isLoading}>
            {isLoading && (
              <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
            )}
            Subscribe
          </button>
        </div>
      </form>
    </div>
  );
}

Has anyone encountered this issue before? Is there something I'm missing in my schema definition?

Any help would be greatly appreciated!

like image 889
HKS Avatar asked Feb 04 '26 19:02

HKS


2 Answers

An input field returns an empty string even when no input is provided. The required_error property in Zod triggers only if the field is not registered, not when it’s an empty string.

To handle this, you can use Zod’s .min(1) validation. This checks that the string length is at least 1, ensuring the field isn’t empty. Here’s how you can adjust your schema:

export const subscribeSchema = z.object({
  email: z
    .string()
    .min(1, { message: "Email is required" }) // Makes sure the email field isn't empty
    .email({ message: "Email is invalid" }), // Checks if it's a valid email
});
like image 81
LuisCA Avatar answered Feb 08 '26 16:02

LuisCA


This solution might help if you are using React-form-hook

You can utilize the setValueAs option in the register function provided by React Hook Form.

<input
register={register("email", {
    setValueAs: (value) => {
        if (value === "") return undefined;
        return value;
    }
})}
name="email"
/>

By setting the value to undefined when it's empty, you can ensure that zod will recognize it as a required field

like image 21
Manikanta prasad Avatar answered Feb 08 '26 14:02

Manikanta prasad



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!