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.otp
object is logged as undefined, and no error is shown in theFormHelperText
.
What I’ve tried:
- Added a
console.log(verifyOtpErrors?.otp)
in theonVerifyOtp
function to debug, butverifyOtpErrors?.otp
is 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])
.