TL;DR Codesandbox to see it in action
Introduction
In this tutorial I will show you how I managed to use multiple Yup validation schemas with react-hook-form.
My use case I had a form which I wanted to save (with all fields required) but also to save as draft (with only one required field)
Step 1: Implement default schema for save
import { useForm } from "react-hook-form"; import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; const schema = yup.object().shape({ firstName: yup.string().required(), lastName: yup.string().required() }); /*...*/ const { register, handleSubmit, formState: { errors } } = useForm<FormData>({ resolver: yupResolver(schema), // default schema }); const onSubmit = handleSubmit((_data) => { alert("schema valid!"); }); <form onSubmit={onSubmit}> <label>First Name</label> <input {...register("firstName")} /> <label>Last Name</label> <input {...register("lastName")} /> <button type="submit"> Save </button> </form>
Step 2: Implement second schema for save as draft
const draftSchema = yup.object().shape({ firstName: yup.string().required(), lastName: yup.string() // lastName is optional for draft }); /*...*/ const saveDraft = async () => { // todo } <form onSubmit={onSubmit}> {/*...*/} <button type="button" onClick={saveDraft}> Save as draft </button> </form
Step 3: saveDraft function
We're going to manually use the yup.validate() to validate the draftSchema when clicking save as draft. And setting the error into react-hook-form.
/*...*/ const { register, handleSubmit, getValues, // to get current form data setError, // to set error from yup.validate() } = useForm<FormData>({ resolver: yupResolver(schema), }); const saveDraft = async () => { const data = getValues(); // try catch block for yup validate function. abortEarly: false so it returns all errors on our form. try { await draftSchema.validate(data, { abortEarly: false }); // if passes, data is valid alert("draft schema valid!"); } catch (error) { // loop over all errors and setError to use them in react-hook-form error.inner?.map((inner, index) => { const { type, path, errors } = inner; return setError(path, { type, message: errors[index] }); }); } }; /*...*/
Conclusion
And that's it! Not the most beautiful solution, but the only one I've got working.
I hope you've found this article useful, please let me know how you used this or another solution in the comments :)
Top comments (4)
Thanks!
I've found more natural way to do it:
Not the yup resolver but you can easily refactor it.
And formState.isSubmitting will properly reflect submit status, but for your solution it will be always false.
@bublinec Glad you found it useful!
This is just what I was looking for, thanks! :)