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"
}
];
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>
);
}
Step 2: Setting up React Hook Form
We need to npm install and import our dependencies
import { useForm } from "react-hook-form";
And setup our useForm
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.
<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.
// 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
// 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!
// 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
const {
formState: { errors },
register
} = useForm({
defaultValues: {},
resolver: yupResolver(dynamicValidationSchema), // 🎉 Here we use our dynamic schema
mode: "onTouched"
});
Top comments (1)
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.