I am using React Hook Form to handle form validation in my React project. While everything works fine when using plain elements, I encounter issues when I try to wrap the inputs in a custom component. Specifically, validations such as minLength and maxLength are not being triggered properly. It is always countering the required validation.
Here’s an example of my setup: Parent Component (Parent.jsx):
import { useForm } from "react-hook-form";
import Input from "./Components/Input.jsx";
import Button from "./Components/Button.jsx";
export default function Parent() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const login = (data) => {
console.log("Form Data:", data);
};
return (
<div className="App">
<div className="container">
<form onSubmit={handleSubmit(login)}>
<Input
type="text"
name="username"
{...register("username", {
required: {
value: true,
message: "Username is required",
},
minLength: {
value: 5,
message: "Too Few Characters",
},
maxLength: {
value: 15,
message: "username length should not exceed 15",
},
})}
/>
{errors.username && <p className="red">{errors.username.message}</p>}
<Input
type="password"
name="password"
{...register("password", {
required: {
value: true,
message: "password is required",
},
minLength: {
value: 6,
message: "Password length should be greater than 6",
},
maxLength: {
value: 15,
message: "Password length should not exceed 15",
},
})}
/>
{errors.password && <p className="error-red">{errors.password.message}</p>}
<Button type="submit" />
</form>
</div>
</div>
);
}
custom Input Component (Input.jsx):
import { forwardRef } from "react";
const Input = forwardRef(function Input(
{ type = "text", name = "", ...props },
ref
) {
return (
<>
<input
placeholder=" "
className="txt-input"
type={type}
ref={ref}
{...props}
/>
{name && (
<label className="label-placeholder">
{name.charAt(0).toUpperCase() + name.slice(1)}
</label>
)}
</>
);
});
export default Input;
The Input component should pass/forward all field props to the underlying input element, e.g. the missing name prop so react-hook-form can validate the field data correctly.
And for accessibility, you may also want to connect the rendered label to the input since it's not wrapping it so when it's clicked/interacted with the input is focused.
Example:
const Input = forwardRef(function Input(
{ name = "", type = "text", ...props },
ref
) {
return (
<>
<input
ref={ref}
className="txt-input"
id={name} // <-- for accessibility with label
name={name} // <-- for field validation
placeholder=" "
type={type}
{...props}
/>
{name && (
<label
className="label-placeholder"
htmlFor={name} // <-- for accessibility with input
>
{name.charAt(0).toUpperCase() + name.slice(1)}
</label>
)}
</>
);
});