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.

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!
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
});
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With