DEV Community

Cover image for React Hook Form: Dynamic Yup validation schema
☀️
☀️

Posted on

React Hook Form: Dynamic Yup validation schema

TL;DR Codesandbox to see it in action

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.

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"
  }
];
Enter fullscreen mode Exit fullscreen mode

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.

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Setting up React Hook Form

We need to npm install and import our dependencies

import { useForm } from "react-hook-form";
Enter fullscreen mode Exit fullscreen mode

And setup our useForm

const {
    formState: { errors },
    register
  } = useForm({
    mode: "onTouched"
  });
Enter fullscreen mode Exit fullscreen mode

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

<input
  placeholder={customField.placeholder}
  {...register(customField.name)}
/>
<span>{errors[customField.name]?.message}</span>
Enter fullscreen mode Exit fullscreen mode

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.

// 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;
    }
  });
};
Enter fullscreen mode Exit fullscreen mode

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

// 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;
  };
Enter fullscreen mode Exit fullscreen mode

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

// 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);
Enter fullscreen mode Exit fullscreen mode

And finally we can use this dynamicValidationSchema in our useForm

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

Top comments (1)

Collapse
 
pcelac profile image
Aleks

This would be useful if you actually mocked API call (promise with 1s delay) to get custom fields. This way, it's not that dynamic, as you have your static data ready.