DEV Community

Cover image for React Hook Form: Multiple Yup validation schemas
☀️
☀️

Posted on

React Hook Form: Multiple Yup validation schemas

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

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

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

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)

Collapse
 
ormr profile image
Serafim Gavrilov

Thanks!

Collapse
 
sega057 profile image
Serhii Trofimenko

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.

 export const draftResolver = zodResolver<zod.ZodType<Partial<YourPayloadType>>>( zod.object({ id: zod.string({ required_error: 'id is required', }), }), ); export const defaultResolver = zodResolver<zod.ZodType<YourPayloadType>>( ... // Inside component ... const isDraft = React.useRef(false); const methods = useForm({ resolver: (...args) => { if (isDraft.current) { return draftResolver(...args); } return defaultResolver(...args); }, defaultValues, }); const onSubmit = (draft?: boolean) => { isDraft.current = !!draft; handleSubmit(async (data) => { // any logic you want to submit the form })(); }; 
Enter fullscreen mode Exit fullscreen mode
Collapse
 
feijens profile image
☀️

@bublinec Glad you found it useful!

Collapse
 
bublinec profile image
Juraj Bublinec

This is just what I was looking for, thanks! :)