Skip to content

Instantly share code, notes, and snippets.

@thepuskar
Last active December 12, 2023 06:45
Show Gist options
  • Save thepuskar/4d57dc50558d8104b993b0644ff46f76 to your computer and use it in GitHub Desktop.
Save thepuskar/4d57dc50558d8104b993b0644ff46f76 to your computer and use it in GitHub Desktop.
React hook form Input custom component with strict typed
import { Input } from "@/components/input-fields";
import { zodResolver } from "@hookform/resolvers/zod";
import Link from "next/link";
import { SubmitHandler, useForm } from "react-hook-form";
import { z } from "zod";
const RegisterSchema = z
.object({
email: z
.string()
.trim()
.min(1, { message: "Email is required" })
.email({ message: "Invalid email address." }),
password: z
.string({ required_error: "Required error message." })
.trim()
.min(1, { message: "Password is required." })
.min(6, { message: "Password must be atleast 6 characters." })
.max(20, { message: "Password must be 20 or fewer characters long." }),
confirmPassword: z
.string()
.trim()
.min(1, { message: "Confirm password is required." }),
})
.refine(
(values) => {
return values?.confirmPassword === values?.password;
},
{
message: "Password did not match!",
path: ["confirmPassword"],
}
);
type RegisterSchemaType = z.infer<typeof RegisterSchema>;
export const RegisterForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<RegisterSchemaType>({
resolver: zodResolver(RegisterSchema),
});
const onSubmit: SubmitHandler<RegisterSchemaType> = (data) => {
console.log("Submit Successfull", data);
};
return (
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form className="space-y-6" onSubmit={handleSubmit(onSubmit)}>
<Input
name="email"
id="email"
type="email"
label="Email address"
placeholder="Enter email address"
register={register}
errors={errors}
/>
<Input
name="password"
id="password"
type="password"
label="Password"
placeholder="Enter password"
register={register}
errors={errors}
/>
<Input
name="confirmPassword"
id="confirmPassword"
type="password"
label="Confirm password"
placeholder="Confirm password"
register={register}
errors={errors}
/>
<div>
<button
type="submit"
className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Register
</button>
</div>
</form>
<p className="mt-10 text-center text-sm text-gray-500">
Already have an account?
<Link
href="login"
className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
>
{" "}
Sign in
</Link>
</p>
</div>
);
};
"use client";
import classNames from "classnames";
import { DetailedHTMLProps, InputHTMLAttributes, useState } from "react";
import { FaEye, FaEyeSlash } from "react-icons/fa";
import {
UseFormRegister,
FieldValues,
FieldErrors,
Path,
RegisterOptions,
FieldName,
} from "react-hook-form";
import {
ErrorMessage,
FieldValuesFromFieldErrors,
} from "@hookform/error-message";
export type InputType = "text" | "email" | "password";
export type InputProps = {
id: string;
name: string;
label?: string;
type?: InputType;
className?: string;
} & Omit<
DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>,
"size"
>;
export type FormInputProps<TFormValues extends FieldValues> = {
name: Path<TFormValues>;
rules?: RegisterOptions;
register?: UseFormRegister<TFormValues>;
errors?: FieldErrors<TFormValues>;
} & Omit<InputProps, "name">;
export const Input = <TFormValues extends Record<string, unknown>>({
className,
name,
register,
rules,
errors,
label,
type,
...props
}: FormInputProps<TFormValues>): JSX.Element => {
const [showPassword, setShowPassword] = useState<boolean>(false);
const togglePassword = () => {
setShowPassword((curr) => !curr);
};
return (
<div>
<div className="flex items-center justify-between">
<label
htmlFor={name}
className="block text-sm font-medium leading-6 text-gray-900"
>
{label}
</label>
</div>
<div className="mt-2 password-div">
<input
name={name}
type={showPassword ? "text" : type}
className={classNames(
"block w-full rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:outline-none focus:ring-gray-600 sm:text-sm sm:leading-6",
{ "ring-red-500 focus:ring-red-500": errors?.[name] }
)}
{...props}
{...(register && register(name))}
/>
{type === "password" ? (
<>
{showPassword ? (
<FaEyeSlash className="eyeIcon" onClick={togglePassword} />
) : (
<FaEye className="eyeIcon" onClick={togglePassword} />
)}
</>
) : null}
</div>
<ErrorMessage
name={
name as unknown as FieldName<
FieldValuesFromFieldErrors<FieldErrors<TFormValues>>
>
}
errors={errors}
render={({ message }) => (
<div className="text-red-500 mt-0.5 text-xs">{message}</div>
)}
/>
</div>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment