I’m using React Hook Form with Zod validation to implement an OTP (One-Time Password) form where users input a 6-digit OTP, with each digit in a separate input field. I’m using z.object to validate that the OTP consists of exactly 6 numeric digits, and I expect validation errors to display when incorrect input is provided or when less than 6 input is submitted .
Here’s my otpSchema and the relevant code:
const otpSchema = z.object({
otp: z
.array(
z.string().regex(/^d$/, 'Each input must be a single digit'), // Ensure it is a number
)
.length(6, 'OTP must consist of exactly 6 digits')
.nonempty({
message: "Can't be empty!",
}),
});
const {
register: verifyOtpRegister, //unused
handleSubmit: verifyOtpSubmit,
trigger: verifyOtpTrigger,
control,
getValues: getVerifyOtpValues,
formState: { errors: verifyOtpErrors },
} = useForm({
resolver: zodResolver(otpSchema),
});
const onVerifyOtp = (data?: any) => {
console.log({ error: verifyOtpErrors.otp });
console.log({ data });
verifyOtp();
};
I’m rendering 6 separate input fields for the OTP, like this:
{[...Array(6)].map((_, index) => (
<Grid item key={index}>
<Controller
name={`otp[${index}]`}
control={control}
render={({ field }) => (
<TextField
{...field}
id={`otp-input-${index}`}
inputMode="numeric"
inputProps={{
maxLength: 1,
pattern: '[0-9]*',
}}
onChange={(e) => {
console.log(e);
handleInputChange(e, index, field);
}}
onKeyDown={(e) => handleKeyDown(e, index)}
value={field.value || ''}
autoComplete="off"
sx={{ width: 40, textAlign: 'center' }}
/>
)}
/>
</Grid>
))}
<FormHelperText error>
{`${verifyOtpErrors?.otp?.message || ''}`}
</FormHelperText>
<button
type="submit"
className="bg-blue-500 text-white py-2 px-4 rounded mt-4"
onClick={() => {
console.log(verifyOtpErrors?.otp);
verifyOtpSubmit(onVerifyOtp);
}}
>
Verify OTP
</button>
The issue:
- When I input the correct valid OTP format (e.g., 123456), the flow works, and the form successfully validates and submits.
- However, when I enter invalid data (e.g., adb123, empty fields, or less/more than 6 digits), the flow doesn’t work as expected but validation doesn’t display any error messages. The
errors.otpobject is logged as undefined, and no error is shown in theFormHelperText.
What I’ve tried:
- Added a
console.log(verifyOtpErrors?.otp)in theonVerifyOtpfunction to debug, butverifyOtpErrors?.otpis always undefined for invalid input.
UsedverifyOtpTrigger('otp')to manually trigger validation, but it still doesn’t populate the error messages and is undefined.
Ensured that the name in Controller matches the schema(otp[index]).
