Reusable form components using react + react hooks form + yup + typescript
Tags: react, javascript, webdev, typescript
Published: 2021-12-21 16:02:07

## Introduction
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.

```jsx
<Form>
 <Input name="email" type="email" />
 <Input name="password" type="password" />
</Form>
```

## What is React hooks form?
A library to build Performant, flexible and extensible forms with easy-to-use validation. Check [Official Website](https://react-hook-form.com/) for more information.

## What is Yup?
Yup is a straightforward JavaScript schema builder for value parsing and validation. 

## Motivation
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.

## Prerequisites
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.

```bash
npx create-react-app my-app --template typescript
```

## Installation
```bash
npm install --save react-hook-form @hookform/resolvers yup
```

## Lets Build
Create 2 Components 
```bash
├── src/
├── components
    ├── Form.tsx
    ├── Input.tsx
    ├── Usage.tsx

```

### Form.tsx
We use this component as a simple form wrapper.

```jsx
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;

```

### Input.tsx
We use this component for any input element (text,password,email,etc)

```jsx
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;

```


### Usage.tsx
Above components can be used in application as follows

```jsx
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](https://codesandbox.io/s/reusable-compoents-react-hooks-form-f594r). Feel free to check.





![COngratulation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ifz4luvk7gm0y1s49rn6.jpg)