React Hook Form: Dynamic Yup validation schema
Tags: react, javascript, tutorial
Published: 2022-03-03 14:41:23

TL;DR [Codesandbox to see it in action](https://codesandbox.io/s/dynamic-yup-schema-for-react-hook-form-qdw03?file=/src/App.js)
## Introduction
In this tutorial I will show you how to create a dynamic Yup validation schema to use with React Hook Form.

In my use case I had to create this because the form in our app is generated in the admin environment and get's delivered to the front end via an API.

## Table of contents
* Show the custom fields
* Setting up React Hook Form
* Creating our dynamic schema


### Step 1: Show the custom fields

The data for our custom fields will most likely come from an API, but for this example I'll add it to a separate file.

```javascript
export const customFields = [
  {
    name: "firstName", // Name should be unique and is our identifier
    label: "Firstname",
    placeholder: "Tommy",
    type: "text" // Type is defined by ourselves, based on this we will add validations
  },
  {
    name: "lastName",
    label: "Lastname",
    placeholder: "Wiseau",
    type: "text"
  },
  {
    name: "website",
    label: "Portfolio",
    placeholder: "https://...",
    type: "url"
  }
];
```


Now that we have our data in place, we can show the fields by looping through them in our React app. 
As you can see I import the data for our separate file at line 2.
```jsx
import React from "react";
import { customFields } from "./customFieldData";

export default function App() {
  return (
    <div className="App">
      <form className="form">
        {customFields.map((customField) => {
          return (
            <div key={customField.name}>
               <label>{customField.label}</label>
               <input
                 placeholder={customField.placeholder}
                 name={customField.name}
               />
            </div>
          );
        })}
      </form>
    </div>
  );
}
```

### Step 2: Setting up React Hook Form

We need to npm install and import our dependencies

```jsx
import { useForm } from "react-hook-form";
```

And setup our useForm
```jsx
const {
    formState: { errors },
    register
  } = useForm({
    mode: "onTouched"
  });
```

And adjust our inputs a bit so they are registered to react hook form. I've also added an error message.

```jsx
<input
  placeholder={customField.placeholder}
  {...register(customField.name)}
/>
<span>{errors[customField.name]?.message}</span>
```

### Step 3: Creating our dynamic schema

First we create a function to extend our custom field data with Yup validations.
This logic will be based on the type of the field, in this case we'll validate the URL fields.

```javascript
// Extend customFields with validation based on type
// As an example we only extend the URL type fields
const useCustomFieldsExtendValidation = (customFields) => {
  return customFields.map((customField) => {
    switch (customField.type) {
      case "url":
        return {
          ...customField,
          validationType: "string",
          validations: [
            {
              type: "trim",
              params: []
            },
            {
              type: "url",
              params: ["Not a valid URL"]
            }
          ]
        };
      default:
        return customField;
    }
  });
};
```

Now that we can extend our form data, we'll create a function to actually create the Yup schema based on this data.
[Much thanks to vijayranghar](https://github.com/jquense/yup/issues/559)

```javascript
// This function creates the dynamic Yup schema
  const useCustomFieldsDynamicSchema = (schema, config) => {
    const { name, validationType, validations = [] } = config;
    if (!yup[validationType]) {
      return schema;
    }
    let validator = yup[validationType]();
    validations.forEach((validation) => {
      const { params, type } = validation;
      if (!validator[type]) {
        return;
      }
      validator = validator[type](...params);
    });
    schema[name] = validator;
    return schema;
  };
```

Now that we have our functions ready, we can use them!

```javascript
// First extend the data with our validations
const dynamicFormData = useCustomFieldsExtendValidation(customFields);


// Create schema based on added validations
const customFieldsSchema = dynamicFormData.reduce(
  useCustomFieldsDynamicSchema,
  {}
);

// Create Yup schema
const dynamicValidationSchema = yup.object().shape(customFieldsSchema);
```

And finally we can use this dynamicValidationSchema in our useForm

```javascript
const {
    formState: { errors },
    register
  } = useForm({
    defaultValues: {},
    resolver: yupResolver(dynamicValidationSchema), // 🎉 Here we use our dynamic schema
    mode: "onTouched"
  });
```