38
Reusable form components using react + react hooks form + yup + typescript
This Post helps to build a simple form with basic elements like input and select using react hooks form that manage form data, submission, and validation. By the end of this post, we will create reusable components with following syntax.
<Form>
<Input name="email" type="email" />
<Input name="password" type="password" />
</Form>
A library to build Performant, flexible and extensible forms with easy-to-use validation. Check Official Website for more information.
Yup is a straightforward JavaScript schema builder for value parsing and validation.
I didn't find many resources online for reusable components for react hooks form, especially using typescript. I have written this blog post to share what I created in few hours. Feel free to comment improvements below.
You can use this library in react and react based frameworks such as NextJS, GatsbyJS and even react native. I will be using a simple typescript project bootstrapped using create-react-app.
npx create-react-app my-app --template typescript
npm install --save react-hook-form @hookform/resolvers yup
Create 2 Components
├── src/
├── components
├── Form.tsx
├── Input.tsx
├── Usage.tsx
We use this component as a simple form wrapper.
import React, { FC, createElement } from "react";
import { ReactNode } from "react";
export type classNameType = string;
export type childrenType = ReactNode;
export interface IFormProps {
defaultValues?: any;
children?: childrenType;
buttonLabel?: string;
onSubmit?: any;
handleSubmit?: any;
register?: any;
className?: classNameType;
}
const Form: FC<IFormProps> = ({
defaultValues,
buttonLabel = "Submit",
children,
onSubmit,
handleSubmit,
register,
...rest
}) => {
return (
<form onSubmit={handleSubmit(onSubmit)} {...rest}>
<div className="d-flex justify-content-center fields__email">
{Array.isArray(children)
? children.map((child) => {
return child.props.name
? createElement(child.type, {
...{
...child.props,
register,
key: child.props.name
}
})
: child;
})
: children}
</div>
<button className="btn btn--brand">{buttonLabel}</button>
</form>
);
};
export default Form;
We use this component for any input element (text,password,email,etc)
import React, { FC, InputHTMLAttributes } from "react";
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
name: string;
label?: string;
error?: string;
register?: any;
wrapperClass?: string;
className?: string;
}
const Input: FC<InputProps> = ({
register,
name,
error,
label,
wrapperClass,
...rest
}) => {
return (
<div className={wrapperClass}>
{label && <label htmlFor={name}>{label}</label>}
<input
aria-invalid={error ? "true" : "false"}
{...register(name)}
{...rest}
/>
{error && <span role="alert">{error}</span>}
</div>
);
};
export default Input;
Above components can be used in application as follows
import React from "react";
import Form from "./Form";
import Input from "./Input";
import * as yup from "yup";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
// interface for form
interface EmailInterface {
email: string;
password: string;
}
// validation
const EmailSchema = yup.object().shape({
email: yup
.string()
.email("Enter a valid email")
.required("Email is required"),
password: yup
.string()
.max(32, "Max password length is 32")
.required("Password is required")
});
const Usage = () => {
const {
register,
handleSubmit,
formState: { errors }
} = useForm({ resolver: yupResolver(EmailSchema) });
const onSubmit = (data: EmailInterface) => console.log(data);
return (
<Form
buttonLabel="Change Email"
register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit}
className="change-form"
>
<Input
name="email"
type="email"
placeholder="Enter your email"
error={errors.email?.message}
autoFocus
/>
<Input
name="password"
type="password"
placeholder="Password"
error={errors.password?.message}
/>
</Form>
);
};
export default Usage;
Congrats! You have successfully created reusable input component using react hooks form, yup and typescript. Here's same project in codesandbox. Feel free to check.
38