A few days ago I applied for a front-end dev job and heard back from the company. They followed up with an email and wanted me to complete a React coding challenge on HackerRank as part of the interview process. The test consisted of only two question and pertained to React. Sounds easy enough, right? Enter stage right: form validation in React The test was timed and only allowed for about 20 minutes of time to fix a semi-built form provided and to add form validation for , , , and input forms. After being tripped up by this task and running out of time I decided it was time to look into form validation libraries for building and validating forms with React. name email phone number url I decided to learn how to use and to validate forms. Formik Yup In this article I will show you how to build a simple and straightforward form with and used for the functionality and validation, and used for the UI. Formik Yup React-Bootstrap This article assumes the reader is familiar with , , and . This article will not go through the ins and outs of building a basic form or styling, the focus will be on form validation with and . React create-react-app npm Formik Yup Building the React-Bootstrap form First let's install and with : React-Bootstrap Styled-Components npm npm install react-bootstrap bootstrapnpm install react-bootstrap bootstrap npm i styled-components After installing and including the stylesheet in our file, let's build a basic form using that includes a name, email, phone, and url input: React-Bootstrap index.html React-Bootstrap import React from 'react'; import styled from 'styled-components'; import { , Button } from 'react- '; CONTAINER = styled.div` background: #F7F9FA; height: auto; width: 90%; margin: 5em auto; color: snow; -webkit-box-shadow: 5px 5px 5px 0px rgba(0, 0, 0, 0.4); -moz-box-shadow: 5px 5px 5px 0px rgba(0, 0, 0, 0.4); box-shadow: 5px 5px 5px 0px rgba(0, 0, 0, 0.4); @media(min-width: 786px) { width: 60%; } { color: #24B9B6; font-size: 1.2em; font-weight: 400; } h1 { color: #24B9B6; padding-top: .5em; } . -group { margin-bottom: 2.5em; } `; MYFORM = styled( )` width: 90%; text-align: left; padding-top: 2em; padding-bottom: 2em; @media(min-width: 786px) { width: 50%; } `; BUTTON = styled(Button)` background: #1863AB; border: none; font-size: 1.2em; font-weight: 400; &:hover { background: #1D3461; } `; BasicForm = () => { ( <CONTAINER> <MYFORM className= > < .Group controlId= > < . >Name :</ . > < .Control = name= placeholder= /> </ .Group> < .Group controlId= > < . >Email :</ . > < .Control = name= placeholder= /> </ .Group> < .Group controlId= > < . >Phone :</ . > < .Control = name= placeholder= /> </ .Group> < .Group controlId= > < . >Blog :</ . > < .Control = name= placeholder= /> </ .Group> <BUTTON variant= = > Submit </BUTTON> </MYFORM> </CONTAINER> ); } export default BasicForm; Form bootstrap // Styled-components styles const label form const Form const const return "mx-auto" Form "formName" Form Label Form Label Form type "text" "name" "Full Name" Form Form "formEmail" Form Label Form Label Form type "text" "email" "Email" Form Form "formPhone" Form Label Form Label Form type "text" "phone" "Phone" Form Form "formBlog" Form Label Form Label Form type "text" "blog" "Blog URL" Form "primary" type "submit" Here's what our form looks like : Now that our basic contact form is built let's install and and start building the validation features: Formik Yup npm i formik npm i yup gives us access to a wealth of form related functionality such as storing the of input forms, , functionality, and . is just an " " that pairs nicely with and will be used to validate the input fields in the form. Formik values error handling onSubmit validation Yup object schema validator Formik After importing and in our component the first thing we need to do is wrap the form itself inside a tag. After wrapping the form within the tag we will set the initial values of the form's inputs using prop. Formik Yup React <Formik> <Formik> Formik's initialValues This consists of setting an object containing all the initial starting values for our form, like this: <Formik initialValues={{ , , , }}> //Sets initial values for form inputs name: "" email: "" phone: "" blog: "" After the initial values are all set we need to call a function within the tag. lists a long list of available helpers. In our case we'll be using , , , , , , & . Formik Formik's documentation values errors touched handleChange handleBlur handleSubmit isSubmitting After initializing our our form and calling the callback function with the necessary parameters our form looks like this: BasicForm = () => { ( <CONTAINER> <Formik initialValues={{ name: , email: , phone: , blog: }}> { } {( {values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( <MYFORM className= > < .Group controlId= > < . >Name :</ . > < .Control = name= placeholder= /> </ .Group> < .Group controlId= > < . >Email :</ . > < .Control = name= placeholder= /> </ .Group> < .Group controlId= > < . >Phone :</ . > < .Control = name= placeholder= /> </ .Group> < .Group controlId= > < . >Blog :</ . > < .Control = name= placeholder= /> </ .Group> <BUTTON variant= = > Submit </BUTTON> </MYFORM> )} </Formik> </CONTAINER> ); } export default BasicForm; const return //Sets initial values for form inputs "" "" "" "" /* Callback function containing Formik state and helpers that handle common form actions */ "mx-auto" Form "formName" Form Label Form Label Form type "text" "name" "Full Name" Form Form "formEmail" Form Label Form Label Form type "text" "email" "Email" Form Form "formPhone" Form Label Form Label Form type "text" "phone" "Phone" Form Form "formBlog" Form Label Form Label Form type "text" "blog" "Blog URL" Form "primary" type "submit" Now that our values are initialized and our callback function contains the proper parameters let's update the field forms and connect them to by adding , , and to our form properties: Formik onChange onBlur value BasicForm = () => { ( <CONTAINER> <Formik initialValues={{ name: , email: , phone: , blog: }}> { } {( {values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( <MYFORM className= > {console. (values)} < .Group controlId= > < . >Name :</ . > < .Control = name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.name} /> </ .Group> < .Group controlId= > < . >Email :</ . > < .Control = name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.email} /> </ .Group> < .Group controlId= > < . >Phone :</ . > < .Control = name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.phone} /> </ .Group> < .Group controlId= > < . >Blog :</ . > < .Control = name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.blog} /> </ .Group> <BUTTON variant= = > Submit </BUTTON> </MYFORM> )} </Formik> </CONTAINER> ); } export default BasicForm; const return //Sets initial values for form inputs "" "" "" "" /* Callback function containing Formik state and helpers that handle common form actions */ "mx-auto" log Form "formName" Form Label Form Label Form type "text" /* This name property is used to access the value of the form element via values.nameOfElement */ "name" "Full Name" /* Set onChange to handleChange */ /* Set onBlur to handleBlur */ /* Store the value of this input in values.name, make sure this is named the same as the name property on the form element */ Form Form "formEmail" Form Label Form Label Form type "text" "email" "Email" Form Form "formPhone" Form Label Form Label Form type "text" "phone" "Phone" Form Form "formBlog" Form Label Form Label Form type "text" "blog" "Blog URL" Form "primary" type "submit" Notice that we can and this will log out all the in our form. console.log(values) values After filling out the form inputs the following is being logged to the console: Creating a Yup schema to validate form input Our form is now hooked up to and is handling the input data for us. The next step is to add validation to our form, this is where comes into play. React-Bootstrap Formik Formik Yup Using we can create an that tells our form what shape our data should have and the requirements we want each input field to have when being validated (i.e. emails having to be in format or names being required to contain at least two letters). Yup object schema name@email.com In order to use we will create a variable and set it equal to a function calling and methods. will contain an object that contains the form input names ( , , , ). This will allow us to use to set validation rules for our inputted elements and looks like this : Yup validationSchema Yup's Object Shape Shape name email phone blog Yup const validationSchema = object .shape({ name: , email: , phone: , blog: }); // Schema for yup . Yup () . Yup string () . Yup string () . Yup string () . Yup string () contains a property for each of our form elements and tells the form that these elements must be of type . offers many different functions for validating form fields. validationSchema Yup.string() string Yup For the sake of this form we will be using , , , , , and . Check out the to view more available functions. min max required email matches url Yup documentation and allow us to set minimum and maximum lengths for inputted text, allows us to make a field mandatory, requires the input to be in valid email form (i.e. ), and requires the input to be a valid URL (i.e. ). min max required email name@email.com url http://www.website.com allows us to pass a variable containing a and check that the inputted form data matches it. We will use and a to check the format of the phone form data. Matches regular expression matches regex All of these validation functions allow us to pass an error message as the second parameter. The complete object looks like this: validationSchema const phoneRegExp = /^(\+?\d{ , })?\s?-?\s?(\(?\d{ }\)?)\s?-?\s?(\(?\d{ }\)?)\s?-?\s?(\(?\d{ }\)?)?$/ const validationSchema = object .shape({ name: .min( , ) .max( , ) .required( ), email: .email( ) .max( , ) .required( ), phone: .matches(phoneRegExp, ) .required( ), blog: .url( ) .required( ) }); // RegEx for phone number validation 0 4 3 3 4 // Schema for yup . Yup () . Yup string () 2 "*Names must have at least 2 characters" 100 "*Names can't be longer than 100 characters" "*Name is required" . Yup string () "*Must be a valid email address" 100 "*Email must be less than 100 characters" "*Email is required" . Yup string () "*Phone number is not valid" "*Phone number required" . Yup string () "*Must enter URL in http://www.example.com format" "*URL required" Hooking up the schema to our Formik form provides our form with a and we can use our variable to pass our schema to our form like this: Formik validationSchema prop Yup validationSchema <Formik initialValues={{ , , , }} validationSchema={validationSchema} > name: "" email: "" phone: "" blog: "" // Hooks up our validationSchema to Formik Applying an error class and turning the input box red when there is an error Now the form is set up to perform validation using the . If we want to turn the form input box red when the user inputs data that doesn't pass our validation tests defined by , we can add a to do that and use a to test if there is an error and apply the class in the event there is an error : validationSchema schema validationSchema CSS class ternary operator <Form.Control type= /* This used access value form element via values.nameOfElement */ = placeholder= /* Set onChange handleChange */ onChange={handleChange} /* Set onBlur handleBlur */ onBlur={handleBlur} /* Store value this input values. , make sure this named same form element */ value={values. } /* Check field (this field) has been touched there an , so add . styles defined CSS (make input box red) */ className={touched. && errors. ? : null} /> "text" name property is to the of the name "name" "Full Name" to to the of in name is the as the name property on the name if the name and if is error if the error class in the the name name "error" The property is one of the parameters called in our callback function and tests if that particular element has been clicked on by the user. tests if the name input field has been clicked by the user and if there is an error validating the inputted data. If there is an error we apply the class of error to the element and in our we change the input box's border to red. touched Formik {touched.name && errors.name ? "error" : null} CSS This will apply this newly added that turns the input box red : ternary operator CSS class { : solid ; } .error border 2px #FF6565 Now when a user enters text that doesn't pass our validation tests the input box turns red : Adding an error message explaining the error Earlier on in our when we used to set rules for validation we included error messages to display explaining the input error to the user. How do we go about displaying the error message under the form element it pertains to? validationSchema Yup We can use the object and the object provided to our form by in the callback function along with a to display the appropriate error message: touch errors Formik ternary operator {touched.name && errors.name ? ( ): } {errors.name} < = > div className "error-message" </ > div null This will apply this newly added that handles the styling of the error message : ternary operator CSS class { : ; : . . ; : ; : absolute; : . ; } .error-message color #FF6565 padding 5em 2em height 1em position font-size 8em By adding a with the appropriate tests to each form input we can add the correct error message from for each form input. ternary operator validateSchema The full form and schema now look like this : phoneRegExp = /^(\+?\ {0,4})?\s?-?\s?(\(?\ {3}\)?)\s?-?\s?(\(?\ {3}\)?)\s?-?\s?(\(?\ {4}\)?)?$/ validationSchema = Yup.object().shape({ name: Yup. () . (2, ) . (100, ) .required( ), email: Yup. () .email( ) . (100, ) .required( ), phone: Yup. () .matches(phoneRegExp, ) .required( ), blog: Yup. () .url( ) .required( ) }); BasicForm = () => { ( <CONTAINER> <Formik initialValues={{ name: , email: , phone: , blog: }} validationSchema={validationSchema} > { } {( {values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( <MYFORM className= > {console. (values)} < .Group controlId= > < . >Name :</ . > < .Control = name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.name} className={touched.name && errors.name ? : null} /> { } {touched.name && errors.name ? ( <div className= >{errors.name}</div> ): null} </ .Group> < .Group controlId= > < . >Email :</ . > < .Control = name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.email} className={touched.email && errors.email ? : null} /> {touched.email && errors.email ? ( <div className= >{errors.email}</div> ): null} </ .Group> < .Group controlId= > < . >Phone :</ . > < .Control = name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.phone} className={touched.phone && errors.phone ? : null} /> {touched.phone && errors.phone ? ( <div className= >{errors.phone}</div> ): null} </ .Group> < .Group controlId= > < . >Blog :</ . > < .Control = name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.blog} className={touched.blog && errors.blog ? : null} /> {touched.blog && errors.blog ? ( <div className= >{errors.blog}</div> ): null} </ .Group> <BUTTON variant= = > Submit </BUTTON> </MYFORM> )} </Formik> </CONTAINER> ); } export default BasicForm; // RegEx for phone number validation const d d d d // Schema for yup const string min "*Names must have at least 2 characters" max "*Names can't be longer than 100 characters" "*Name is required" string "*Must be a valid email address" max "*Email must be less than 100 characters" "*Email is required" string "*Phone number is not valid" "*Phone number required" string "*Must enter URL in http://www.example.com format" "*URL required" const return //Sets initial values for form inputs "" "" "" "" // Hooks up our validationSchema to Formik /* Callback function containing Formik state and helpers that handle common form actions */ "mx-auto" log Form "formName" Form Label Form Label Form type "text" /* This name property is used to access the value of the form element via values.nameOfElement */ "name" "Full Name" /* Set onChange to handleChange */ /* Set onBlur to handleBlur */ /* Store the value of this input in values.name, make sure this is named the same as the name property on the form element */ /* Check if the name field (this field) has been touched and if there is an error, if so add the .error class styles defined in the CSS (make the input box red) */ "error" /* Applies the proper error message from validateSchema when the user has clicked the element and there is an error, also applies the .error-message CSS class for styling */ "error-message" Form Form "formEmail" Form Label Form Label Form type "text" "email" "Email" "error" "error-message" Form Form "formPhone" Form Label Form Label Form type "text" "phone" "Phone" "error" "error-message" Form Form "formBlog" Form Label Form Label Form type "text" "blog" "Blog URL" "error" "error-message" Form "primary" type "submit" Now when a user inputs data that doesn't pass our validation test the error message we defined earlier in the is displayed. schema The end result looks like this : Handling form submission with Formik Last but not least our form needs to the form when the user clicks the submit button. provides us with an prop that takes care of this. We pass the prop the values and also and . will be set to when the user is submitting the form and will be called after the form is submitted to clear the form post submission. submit Formik onSubmit onSubmit setSubmitting resetForm setSubmitting true resetForm Our tag, with an function now looks like this: Formik onSubmit <Formik initialValues={{ name: , email: , phone: , blog: }} validationSchema={validationSchema} onSubmit={(values, {setSubmitting, resetForm}) => { // button submits form form the process submitting, submit button disabled setSubmitting( ); // Resets form after submission complete resetForm(); // Sets setSubmitting to after form reset setSubmitting( ); }} > "" "" "" "" When and is in of is true is false is false In order to call this function our form has to have an prop set to : onSubmit onSubmit handleSubmit < = = > MYFORM onSubmit {handleSubmit} className "mx-auto" In addition to handling the submission of the form we can also disable the submit button while the form is being submitted so that after the user clicks submit the submit button is while the form is in the process of submitting. disabled We do this by adding a property to the button and setting it equal to : disabled isSubmitting Submit < = = = > BUTTON variant "primary" type "submit" disabled {isSubmitting} </ > BUTTON Now the form when the user clicks the submit button, while the form is being submitted the button is inactive, and after submission is complete the form resets and clears the fields. submits Simulating submission and viewing what the form is submitting In a full stack application this is when the form would be submitted via a . For the sake of viewing the data our form is submitting in this database-less example we can the values when the user the form. POST request alert submits Add a function that has a .5 second delay and contains an with a function that prints out the form data when the form is . setTimeout alert JSON.stringify submitted We can also move the and functions inside this function so they are called after a .5 second delay: resetForm setSubmitting setTimeout // Simulate submitting , shows us submitted, resets form setTimeout(() => { alert( .stringify( , , )); resetForm(); setSubmitting( ); }, ); to database values JSON values null 2 false 500 Our contact form is now complete. The component, in it's entirety, looks like this: React from ; styled from ; { Form, Button } from ; { Formik, ErrorMessage } from ; * as Yup from ; CONTAINER = styled.div` : #F7F9FA; : auto; : %; margin: em auto; : snow; -webkit- -shadow: px px px px rgba( , , , ); -moz- -shadow: px px px px rgba( , , , ); -shadow: px px px px rgba( , , , ); @media( - : px) { : %; } label { : # B9B6; font- : em; font-weight: ; } h1 { : # B9B6; padding-top: em; } .form-group { margin-bottom: em; } .error { border: px solid #FF6565; } .error-message { : #FF6565; padding: em em; : em; position: absolute; font- : em; } `; MYFORM = styled(Form)` : %; -align: left; padding-top: em; padding-bottom: em; @media( - : px) { : %; } `; BUTTON = styled(Button)` : # AB; border: none; font- : em; font-weight: ; &:hover { : # D3461; } `; phoneRegExp = /^(\+?\d{ , })?\s?-?\s?(\(?\d{ }\)?)\s?-?\s?(\(?\d{ }\)?)\s?-?\s?(\(?\d{ }\)?)?$/ validationSchema = Yup.object(). ({ name: Yup.string() . ( , ) . ( , ) .required( ), email: Yup.string() .email( ) . ( , ) .required( ), phone: Yup.string() .matches(phoneRegExp, ) .required( ), blog: Yup.string() .url( ) .required( ) }); BasicForm = () => { ( <CONTAINER> <Formik initialValues={{ name: , email: , phone: , blog: }} validationSchema={validationSchema} onSubmit={(values, {setSubmitting, resetForm}) => { setSubmitting( ); setTimeout(() => { alert(JSON.stringify(values, , )); resetForm(); setSubmitting( ); }, ); }} > { } {( {values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( <MYFORM onSubmit={handleSubmit} className= > {console. (values)} <Form.Group controlId= > <Form.Label>Name :</Form.Label> <Form.Control type= name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.name} className={touched.name && errors.name ? : } /> { } {touched.name && errors.name ? ( <div className= >{errors.name}</div> ): } </Form.Group> <Form.Group controlId= > <Form.Label>Email :</Form.Label> <Form.Control type= name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.email} className={touched.email && errors.email ? : } /> {touched.email && errors.email ? ( <div className= >{errors.email}</div> ): } </Form.Group> <Form.Group controlId= > <Form.Label>Phone :</Form.Label> <Form.Control type= name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.phone} className={touched.phone && errors.phone ? : } /> {touched.phone && errors.phone ? ( <div className= >{errors.phone}</div> ): } </Form.Group> <Form.Group controlId= > <Form.Label>Blog :</Form.Label> <Form.Control type= name= placeholder= onChange={handleChange} onBlur={handleBlur} value={values.blog} className={touched.blog && errors.blog ? : } /> {touched.blog && errors.blog ? ( <div className= >{errors.blog}</div> ): } </Form.Group> <BUTTON variant= type= disabled={isSubmitting}> Submit </BUTTON> </MYFORM> )} </Formik> </CONTAINER> ); } export BasicForm; import 'react' import 'styled-components' import 'react-bootstrap' import 'formik' import 'yup' const background height width 90 5 color box 5 5 5 0 0 0 0 0.4 box 5 5 5 0 0 0 0 0.4 box 5 5 5 0 0 0 0 0.4 min width 786 width 60 color 24 size 1.2 400 color 24 .5 2.5 2 color .5 .2 height 1 size .8 const width 90 text 2 2 min width 786 width 50 const background 1863 size 1.2 400 background 1 // RegEx for phone number validation const 0 4 3 3 4 // Schema for yup const shape min 2 "*Names must have at least 2 characters" max 100 "*Names can't be longer than 100 characters" "*Name is required" "*Must be a valid email address" max 100 "*Email must be less than 100 characters" "*Email is required" "*Phone number is not valid" "*Phone number required" "*Must enter URL in http://www.example.com format" "*URL required" const return //Sets initial values for form inputs "" "" "" "" // When button submits form and form is in the process of submitting, submit button is disabled true // Simulate submitting to database, shows us values submitted, resets form null 2 false 500 /* Callback function containing Formik state and helpers that handle common form actions */ "mx-auto" log "formName" "text" /* This name property is used to access the value of the form element via values.nameOfElement */ "name" "Full Name" /* Set onChange to handleChange */ /* Set onBlur to handleBlur */ /* Store the value of this input in values.name, make sure this is named the same as the name property on the form element */ /* Check if the name field (this field) has been touched and if there is an error, if so add the .error class styles defined in the CSS (make the input box red) */ "error" null /* Applies the proper error message from validateSchema when the user has clicked the element and there is an error, also applies the .error-message CSS class for styling */ "error-message" null "formEmail" "text" "email" "Email" "error" null "error-message" null "formPhone" "text" "phone" "Phone" "error" null "error-message" null "formBlog" "text" "blog" "Blog URL" "error" null "error-message" null "primary" "submit" default Viewing the example form online You can view the or check out the code in the for this project. form online here Github Repo We have now successfully built a form with validation features in React with a minimal amount of pain and suffering.