I love yup and so does my entire team. We actually originally fell in love with it while using formik from Jared Palmer; formik has built in support for yup through its validationSchema field. For a recent project we needed to build really complex forms and felt formik wasn't really up to the task - lots of nested fields, arrays of complex fields, etc, but we didn't want to lose yup.

In comes redux-form-yup

In using yup with redux-form, we were repeating a lot of the same logic... write a function that takes the schema and runs the validation against it returning it in the same structure as the schema and values. So I decided to DRY everything up and bundle it together to make our lives, and maybe yours, a little easier.

What does it do?

redux-form-yup is very simple. It's simply a function that takes in a schema and returns another function that acts as your validation function so you can simply plug it into your HoC or use it as a prop in your form component.

const asyncValidate = <T>(schema: Schema<T>) => {
    return async (values) => {
        return await schema
            .validate(values, {abortEarly: false})
            .then(() => {})
            .catch((errors) => {
                const formErrors = {};
                errors.inner.forEach((error) => {
                    set(formErrors, error.path, error.message);
                });
                return formErrors;
            });
    }
}

This is pretty much it but we can break it down a bit further.

redux-form async validate

asyncValidate returns an async (promise based) function that takes the values from the asyncValidate from redux-form and calls validate on the schema. The option {abortEarly: false} means not to abort the validation at the first error so we can get all of them on the form. The empty object returned from the promise resolve is to tell redux-form that there are no errors - it likes it this way. Finally, if there are any errors we want to map them back to their originating path. We look over each response and use object-set's set function which takes the path of the error and populates the object with the error message. This returns an object that matches our schema and should match the errors properly if you setup your form and schema to match.

Building a schema

The argument schema is your yup schema that you will define for your form. This schema should match your expected values from your form. So you might define a schema like this one:

const schema = yup
    .object()
    .shape({ 
        firstName: yup.string().required(), 
        lastName: yup.string().required(),
        email: yup.string().email().required(),
        address: yup.object().shape({
            street: yup.string.required(),
            city: yup.string.required(),
            state: yup.string.required().min(2).max(2),
            zip: yup.number(),
        })
    });

The schema might describe a form that looks like:

  <Field name="firstName" component={customRender} type="text" />
    <Field name="lastName" component={customRender} type="text" />
    <Field name="email" component={customRender} type="text" />
    <FormSection name="address">
        <Field name="street" component={customRender} type="text" />
        <Field name="city" component={customRender} type="text" />
        <Field name="state" component={customRender} type="text" />
        <Field name="zip" component={customRender} type="text" />
    </FormSection>

Install

The best way to use redux-form-yup is to add it to your redux-form HoC. shouldAsyncValidate forces the validation function to fire - in the future additional options will be added to control when the validation should occur.

import {asyncValidate, shouldAsyncValidate} from "redux-form-yup"

export default reduxForm({
    form: "contact",
    asyncValidate: asyncValidate(schema),
    shouldAsyncValidate,
})(ContactForm);

<3 TypeScript

redux-form-yup is built with TypeScript so its own types are bundled but you will need types for redux-form and yup. redux-form types are available as @types/redux-form from DefinitelyTyped. yup doesn't have types yet but there is a PR open with DefinitelyTyped to add them #21857. For now, I've bundled my own yup typings in this project.

Async/Await & Promises

redux-form asyncValidate is promise driven as such redux-form-yup requires Promise to be available. For older browsers I suggest using es6-promise global installer require('es6-promise/auto');.