Validation
Formik is designed to manage forms with complex validation with ease. Formik supports synchronous and asynchronous form-level and field-level validation. Furthermore, it comes with baked-in support for schema-based form-level validation through Yup. This guide will describe the ins and outs of all of the above.
#
Flavors of Validation#
Form-level ValidationForm-level validation is useful because you have complete access to all of your form's values
and props whenever the function runs, so you can validate dependent fields at the same time.
There are 2 ways to do form-level validation with Formik:
<Formik validate>
andwithFormik({ validate: ... })
<Formik validationSchema>
andwithFormik({ validationSchema: ... })
validate
#
<Formik>
and withFormik()
take a prop/option called validate
that accepts either a synchronous or asynchronous function.
// Synchronous validationconst validate = (values, props /* only available when using withFormik */) => { const errors = {};
if (!values.email) { errors.email = 'Required'; } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) { errors.email = 'Invalid email address'; }
//...
return errors;};
// Async Validationconst sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const validate = (values, props /* only available when using withFormik */) => { return sleep(2000).then(() => { const errors = {}; if (['admin', 'null', 'god'].includes(values.username)) { errors.username = 'Nice try'; } // ... return errors; });};
For more information about <Formik validate>
, see the API reference.
validationSchema
#
As you can see above, validation is left up to you. Feel free to write your own
validators or use a 3rd party library. At The Palmer Group, we use
Yup for object schema validation. It has an
API that's pretty similar to Joi and
React PropTypes but is small enough
for the browser and fast enough for runtime usage. Because we ❤️ Yup sooo
much, Formik has a special config option / prop for Yup object schemas called validationSchema
which will automatically transform Yup's validation errors into a pretty object whose keys match values
and touched
. This symmetry makes it easy to manage business logic around error messages.
To add Yup to your project, install it from NPM.
npm install yup --save
import React from 'react';import { Formik, Form, Field } from 'formik';import * as Yup from 'yup';
const SignupSchema = Yup.object().shape({ firstName: Yup.string() .min(2, 'Too Short!') .max(50, 'Too Long!') .required('Required'), lastName: Yup.string() .min(2, 'Too Short!') .max(50, 'Too Long!') .required('Required'), email: Yup.string().email('Invalid email').required('Required'),});
export const ValidationSchemaExample = () => ( <div> <h1>Signup</h1> <Formik initialValues={{ firstName: '', lastName: '', email: '', }} validationSchema={SignupSchema} onSubmit={values => { // same shape as initial values console.log(values); }} > {({ errors, touched }) => ( <Form> <Field name="firstName" /> {errors.firstName && touched.firstName ? ( <div>{errors.firstName}</div> ) : null} <Field name="lastName" /> {errors.lastName && touched.lastName ? ( <div>{errors.lastName}</div> ) : null} <Field name="email" type="email" /> {errors.email && touched.email ? <div>{errors.email}</div> : null} <button type="submit">Submit</button> </Form> )} </Formik> </div>);
For more information about <Formik validationSchema>
, see the API reference.
#
Field-level Validationvalidate
#
Formik supports field-level validation via the validate
prop of <Field>
/<FastField>
components or useField
hook. This function can be synchronous or asynchronous (return a Promise). It will run after any onChange
and onBlur
by default. This behavior can be altered at the top level <Formik/>
component using the validateOnChange
and validateOnBlur
props respectively. In addition to change/blur, all field-level validations are run at the beginning of a submission attempt and then the results are deeply merged with any top-level validation results.
Note: The
<Field>/<FastField>
components'validate
function will only be executed on mounted fields. That is to say, if any of your fields unmount during the flow of your form (e.g. Material-UI's<Tabs>
unmounts the previous<Tab>
your user was on), those fields will not be validated during form validation/submission.
import React from 'react';import { Formik, Form, Field } from 'formik';
function validateEmail(value) { let error; if (!value) { error = 'Required'; } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) { error = 'Invalid email address'; } return error;}
function validateUsername(value) { let error; if (value === 'admin') { error = 'Nice try!'; } return error;}
export const FieldLevelValidationExample = () => ( <div> <h1>Signup</h1> <Formik initialValues={{ username: '', email: '', }} onSubmit={values => { // same shape as initial values console.log(values); }} > {({ errors, touched, isValidating }) => ( <Form> <Field name="email" validate={validateEmail} /> {errors.email && touched.email && <div>{errors.email}</div>}
<Field name="username" validate={validateUsername} /> {errors.username && touched.username && <div>{errors.username}</div>}
<button type="submit">Submit</button> </Form> )} </Formik> </div>);
#
Manually Triggering ValidationYou can manually trigger both form-level and field-level validation with Formik using the validateForm
and validateField
methods respectively.
import React from 'react';import { Formik, Form, Field } from 'formik';
function validateEmail(value) { let error; if (!value) { error = 'Required'; } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) { error = 'Invalid email address'; } return error;}
function validateUsername(value) { let error; if (value === 'admin') { error = 'Nice try!'; } return error;}
export const FieldLevelValidationExample = () => ( <div> <h1>Signup</h1> <Formik initialValues={{ username: '', email: '', }} onSubmit={values => { // same shape as initial values console.log(values); }} > {({ errors, touched, validateField, validateForm }) => ( <Form> <Field name="email" validate={validateEmail} /> {errors.email && touched.email && <div>{errors.email}</div>}
<Field name="username" validate={validateUsername} /> {errors.username && touched.username && <div>{errors.username}</div>} {/** Trigger field-level validation imperatively */} <button type="button" onClick={() => validateField('username')}> Check Username </button> {/** Trigger form-level validation imperatively */} <button type="button" onClick={() => validateForm().then(() => console.log('blah'))} > Validate All </button> <button type="submit">Submit</button> </Form> )} </Formik> </div>);
#
When Does Validation Run?You can control when Formik runs validation by changing the values of <Formik validateOnChange>
and/or <Formik validateOnBlur>
props depending on your needs. By default, Formik will run validation methods as follows:
After "change" events/methods (things that updatevalues
)
handleChange
setFieldValue
setValues
After "blur" events/methods (things that update touched
)
handleBlur
setTouched
setFieldTouched
Whenever submission is attempted
handleSubmit
submitForm
There are also imperative helper methods provided to you via Formik's render/injected props which you can use to imperatively call validation.
validateForm
validateField
#
Displaying Error MessagesError messages are dependent on the form's validation. If an error exists, and the validation function produces an error object (as it should) with a matching shape to our values/initialValues, dependent field errors can be accessed from the errors object.
import React from 'react';import { Formik, Form, Field } from 'formik';import * as Yup from 'yup';
const DisplayingErrorMessagesSchema = Yup.object().shape({ username: Yup.string() .min(2, 'Too Short!') .max(50, 'Too Long!') .required('Required'), email: Yup.string().email('Invalid email').required('Required'),});
export const DisplayingErrorMessagesExample = () => ( <div> <h1>Displaying Error Messages</h1> <Formik initialValues={{ username: '', email: '', }} validationSchema={DisplayingErrorMessagesSchema} onSubmit={values => { // same shape as initial values console.log(values); }} > {({ errors, touched }) => ( <Form> <Field name="username" /> {/* If this field has been touched, and it contains an error, display it */} {touched.username && errors.username && <div>{errors.username}</div>} <Field name="email" /> {/* If this field has been touched, and it contains an error, display it */} {touched.email && errors.email && <div>{errors.email}</div>} <button type="submit">Submit</button> </Form> )} </Formik> </div>);
The ErrorMessage component can also be used to display error messages.
#
Frequently Asked QuestionsHow do I determine if my form is validating?
If isValidating
prop is true
Can I return `null` as an error message?
No. Use undefined
instead. Formik uses undefined
to represent empty states. If you use null
, several parts of Formik's computed props (e.g. isValid
for example), will not work as expected.
How do I test validation?
Formik has extensive unit tests for Yup validation so you do not need to test that. However, if you are rolling your own validation functions, you should simply unit test those. If you do need to test Formik's execution you should use the imperative validateForm
and validateField
methods respectively.