60

Below is my React form validation code in which I am using formik. By default when the form loads, I want to keep the submit button disabled:

import { useFormik } from "formik";
import * as Yup from "yup";

const formik = useFormik({
    initialValues: {
      firstName: "",
      lastName: "",
      email: ""
    },
    validationSchema: Yup.object({
      firstName: Yup.string()
        .max(15, "Must be 15 characters or less")
        .min(3, "Must be at least 3 characters")
        .required("Required"),
      lastName: Yup.string()
        .min(3, "Must be at least 3 characters")
        .max(20, "Must be 20 characters or less")
        .required("Required"),
      email: Yup.string()
        .email("Invalid email address")
        .required("Required")
    }),
    onSubmit: values => {
      handleSubmit(values);
    }
  });

I have tried to use this on my button:

 disabled={!formik.isValid}

But it only actually works if I try to submit the form. So, if I leave the form blank and hit submit, all the validation errors show up and then the button is disabled. But, it should be disabled already from the start. I checked the documentation but didn't see anything obvious there.

5 Answers 5

126

If you want to keep the submit button disabled initially when the form loads, you can use the use the dirty : boolean property of Formik something as below:

disabled={!formik.dirty}

If you want to keep the submit button disabled until all the field values are valid then you can use isValid: boolean which works as below:

Returns true if there are no errors (i.e. the errors object is empty) and false otherwise.

So you may use it as:

disabled={!formik.isValid}

Now, if you want the submit button to be disabled until all the fields are valid and if the fields values have been changed from their initial values then you would have to use both of the above attributes in conjunction as below:

disabled={!(formik.isValid && formik.dirty)}
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks, this almost works, but not quite. When the page first loads the button is disabled. However, as soon as one text field is correct the button is enabled. This should only happen once all fields are filled in correctly.
I have edited my answer and added some more useful code. Please let me know if this works and please consider accepting my answer if it has helped you :)
If there is some initial value that is valid, button will never be enabled.
As @SashaOmelchenko pointed out, this does not validate initialValues. Use validateOnMount with isValid instead. See stackoverflow.com/a/61896771/1631828
I do exactly like this, but isValid toggles true/false literally after every type of keyboard. Does anybody face this problem too?
|
16

To have the button initially disabled just check if the touch object is empty and keep it this way until all the fields are validated with !isValid

disabled={!isValid || (Object.keys(touched).length === 0 && touched.constructor === Object)}

1 Comment

Both this and the accepted answer are great solutions. I'd like to mention that you should also include isSubmitting to also disable button clicks during submission. disabled={!isValid || isSubmitting || (Object.keys(touched).length === 0 && touched.constructor === Object)}
7

You can add validateOnMount and set it to true

const formik = useFormik({
    validateOnMount: true, // <================= Add this
    initialValues: {
      firstName: "",
      lastName: "",
      email: ""
    },
    validationSchema: Yup.object({
      firstName: Yup.string()
        .max(15, "Must be 15 characters or less")
        .min(3, "Must be at least 3 characters")
        .required("Required"),
      lastName: Yup.string()
        .min(3, "Must be at least 3 characters")
        .max(20, "Must be 20 characters or less")
        .required("Required"),
      email: Yup.string()
        .email("Invalid email address")
        .required("Required")
    }),
    onSubmit: values => {
      handleSubmit(values);
    }
  });

Use on button

disabled={!formik.isValid}

<button type="submit" disabled={!formik.isValid}>
  Submit
</button>

To avoid the form appearing invalid on load, check whether the fields have been touched by using formik.touched

<input
  type="text"
  name="firstName"
  onChange={formik.handleChange}
  onBlur={formik.handleBlur} // <==== This ensures the field is marked as touched on blur
  value={formik.values.firstName}
/>

{formik.touched.firstName && formik.errors.firstName && (
  <p className="text-danger">{formik.errors.firstName}</p>
)}

2 Comments

The only issue with validateOnMount is, it shoes the mandatory fields in red (error). How to encounter that?
@Tarun I've updated the answer
3

Formik keeps track of field values and errors however exposes them for your use, this used to be done via formProps using the render props pattern however now seems to be part of the formik variable returned by the useFormik hook.

I would recomend to start by removing the initial values to a constant. Then you need to access the formik's error object. I have not done this using the new hook syntax, however, looking at the docs I would expect "formik.errors" to work (this is exposed in formProps.errors using render props). Finally the submit buttion disabled should be a check that either formik.values is equal to the initial values OR the errors object is not empty.

Comments

0

I'm using

"formik": "^2.2.6"

and in my case I did it like this. I was not interested to show the messages in the validation messages in initial so I did in this way.

I'm using class based components

<Formik
......
validateOnBlur={true}
validateOnChange={true}
{({handleChange, values, touched, errors, handleBlur }) => (<>
    ..........
    <Form.Control
        placeholder="Name"
        type="text"
        name="field_dba"
        value={values.name}
        onChange={handleChange}
        onFocus={handleBlur} // It plays a role to change the touched property 
        isInvalid={touched.name && !!errors.name}
        isValid={!errors.name}
        className="form-input"
    />
    <label className="modal-lab">Name *</label>
    // Here i'm checking the field is touched or not 
    {touched.name ? <Form.Control.Feedback type="invalid" className="text-capitalize"> {errors.name} </Form.Control.Feedback> : null}

at the end for disabling the button till it's all validated. I'm using this

<button disabled={Array.isArray(errors) || Object.values(errors).toString() != ""} type="submit"> Submit </button>

If this will work with same version please do a upvote.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.