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! :)