Forms are a great way to help you capture your website visitors' details- their names, email addresses, preferences, comments, and feedback. As developers, it's our job to make sure that when users interact with the forms we've created, the data they send is the way we expect it to be.
Formik is a React and React Native library that helps you create forms in React without difficulty. You can pair Formik with validation libraries like Yup to make the process even simpler.
In this article, we will learn how to handle form validation in React using Formik and Yup. We will set up form validation using Yup and Formik’s custom components and understand how Yup works well with Formik in handling Form validation.
Prerequisites
Basic understanding of React JS
Basic understanding of React JS Forms
Project Setup
In this section, you will set up your website using Create React App (CRA) and install some dependencies for the sake of the tutorial. If you already have a React app set up you can skip this part.
- In your terminal, run the following command to create a new React App with CRA:
npx create-react-app practice-forms
We are calling the website practice-forms
because it's the name we would like to give to our React web app, but you can change it to whatever you want.
- Once the installation is done, navigate to the newly created directory:
cd react-forms
- Then, install Tailwind CSS to add some styling to your website:
npm install -D tailwindcss postcss autoprefixer
- To set up Tailwind CSS, edit the file
tailwind.config.js
with the following content:
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
- Also, replace the content of
src/index.css
with the following:
@tailwind base;
@tailwind components;
@tailwind utilities;
Creating the Form with Formik
- Let us proceed by using Formik to create a form. Start by installing Formik:
npm i formik
- Replace the content of
src/App.js
with the following:
import { useFormik } from 'formik';
function App() {
const country = ['United States', 'Japan', 'France', 'other'];
//TODO create formik instance
return (
<div className="bg-blue-300 min-w-screen min-h-screen overflow-x-hidden">
</div>
);
}
export default App;
All you did here was create the component App which does nothing special at the moment.
Notice how we imported the useFormik
hook at the beginning of the file. You will use this hook to create a Formik instance with all the states and helpers you'll need.
The useFormik
hook accepts as a parameter an object of configurations. These configurations can be used to modify and shape your form as you need.
Furthermore, you will pass the following properties in the object:
initialValues
: includes the form fields and their initial values.validationSchema
: A Yup schema to validate the fields. You will understand the need for this shortly.onSubmit
: a function to execute when the form is submitted.
Replace the TODO
in the App
component with the following:
const formik = useFormik({
initialValues: {
name: '',
email: '',
countries: country[0],
age: '',
},
onSubmit: function (values) {
alert(`You are registered! Name: ${values.name}. Email: ${values.email}. countries: ${values.countries}.
Age: ${values.age}`);
}
})
In the code block above, we set the value of the property initialValues
to an object. This object's keys are the names of the fields in the form. Their values are the initial values.
In the onSubmit
function, we receive the values
object as a parameter. Note that the onSubmit
function is only executed once the form is validated. We don't need to perform any validation inside this function.
Now, we can use the formik
variable to create a form, then link its fields to the fields we defined in useFormik
, also we have to link the validation and the submit handler.
Some properties of formik
include:
handleSubmit
: the submit function that should be called when the form is submitted. This is usually assigned to theonSubmit
event handler ofform
elements.errors
: An object that has the field names as properties and the value of each is the error message resulting from validating that field if there are any errors.touched
: An object that has the field names as properties and the value is a boolean indicating whether the user has interacted with the field or not.values
: An object that has the field names as properties and the value of each is the current value of that field. It's usually used to set thevalue
property of input elements.handleChange
: A function that should be used as the handler of the change event of input elements. It's passed as the value of theonChange
prop of elements.handleBlur
: A function that should be used as the handler of the blur event of input elements. It's passed as the value of theonBlur
prop of elements.
- Replace the return statement in
App
with the following:
return (
<div className="bg-blue-300 min-w-screen min-h-screen overflow-x-hidden">
<form onSubmit={formik.handleSubmit} className="max-w-lg mx-auto bg-white rounded shadow-lg mt-7 p-3">
<h1 className='text-3xl mb-3 text-center'>Register</h1>
<div className='mb-4'>
<label for="name">Full Name</label>
<input type="text" name="name" id="name"
className={`block w-full rounded border py-1 px-2 ${formik.touched.name && formik.errors.name ? 'border-red-400' : 'border-gray-300'}`}
onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.name} />
{formik.touched.name && formik.errors.name && (
<span className='text-red-400'>{formik.errors.name}</span>
)}
</div>
<div className='mb-4'>
<label for="email">Email</label>
<input type="email" name="email" id="email"
className={`block w-full rounded border py-1 px-2 ${formik.touched.email && formik.errors.email ? 'border-red-400' : 'border-gray-300'}`}
onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.email} />
{formik.touched.email && formik.errors.email && (
<span className='text-red-400'>{formik.errors.email}</span>
)}
</div>
<div className='mb-4'>
<label for="country">country</label>
<select name="country" id="country"
className={`block w-full rounded border py-1 px-2 ${formik.touched.country&& formik.errors.country? 'border-red-400' : 'border-gray-300'}`}
onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.country} >
{country.map((country, index) => (
<option value={country} key={index}>{country}</option>
))}
</select>
{formik.touched.country&& formik.errors.country&& (
<span className='text-red-400'>{formik.errors.country}</span>
)}
</div>
<div className='mb-4'>
<label for="age">Age</label>
<input type="number" name="age" id="age"
className={`block w-full rounded border py-1 px-2 ${formik.touched.age && formik.errors.age ? 'border-red-400' : 'border-gray-300'}`}
onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.age} />
{formik.touched.age && formik.errors.age && (
<span className='text-red-400'>{formik.errors.age}</span>
)}
</div>
<div className='text-center'>
<button className='bg-blue-500 rounded p-3 text-white' type='submit'>Submit</button>
</div>
</form>
</div>
);
Notice how we used all the properties in the formik
variable mentioned earlier. Here, we pass in the handleChange
helper functions to the inputs’ onChange
attribute.
Running the program
Voila! We've created the form and it can be viewed by running the command below on our terminal.
npm start
You can then open the website at localhost:3000
(default port). If you open the website, you will see the form with 4 fields.
Adding Validation with Yup
In this section, we will be taking you through how you can add validation to the form using Yup.
First, we need to install Yup. Run the following in your terminal:
npm i yup
Yup has a lot of methods and validation rules you can use. The way it works with Formik is dynamic, we need to create a validation schema and pass it to useFormik
as a value to the property validationSchema
.
Yup validation schemas are created using Yup.object
method which takes as a parameter, an object. This object has the field names as properties and their values are validation rules from the Yup library.
- Import Yup at the beginning of
src/App.js
:
import * as Yup from 'yup';
- Then, let's add the property
validationSchema
to the object passed touseFormik
with the following value:
const formik = useFormik({
...,
validationSchema: Yup.object({
name: Yup.string()
.label('Full Name')
.required(),
email: Yup.string()
.email()
.required(),
country: Yup.string()
.oneOf(countries, 'The country you chose does not exist'),
age: Yup.number()
.min(18, 'You need to be older than 18 to register')
.required()
})
})
You add the following validation rules:
name
: Should be a string and we set it to arequired
field i.e. users must provide an answer for the field. We also used thelabel
method to ensure that when the error message is shown it refers to the field as "Full Name". By default, the fields are referred to by the field name, which in this case isname
.email
: Should be a string, an email, and the field isrequired
.country
: Should be a string and one of the values in thecountries
array. You also pass a message as a second parameter tooneOf
which will be the message that shows in case there's an error. It's also required.age
: Should be a number and at least 18. If the age is less than 18, the message "You need to be older than 18 to register" will show. It's also required.
Running the output
Run the server again. Proceed to interact with the form. If you enter values that don't comply with the rules you set in the validation schema, an error will be displayed in red and you won't be able to submit the form until you fill the form correctly.
Custom Validation Rules
Although Yup has helpful validation rules that are readily available for developers, there is also flexibility for us to create custom validation rules. You can use the test function to add a custom rule.
In this section, you will learn how to add a rule to make sure that the name
field has both first and last names.
Change the name
property inside the validationSchema
to the following:
const formik = useFormik({
...,
validationSchema: Yup.object({
name: Yup.string()
.label('Full Name')
.required()
.test('is-full-name', 'Please enter both your first and last name', function (value) {
const nameArr = value.split(" ");
return nameArr.length >= 2;
}),
...
})
})
The first parameter is the name of the custom rule. The second parameter is the message to show in case the field is invalid. The third parameter is the function that determines whether the field is valid or not. It should return a boolean value. If the value is true
, then the field is valid. Otherwise, it's invalid.
Also, you validate the name field to contain both first and last names by splitting it on the space delimiter which will return an array. Proceed to check the array length, if it's at least 2, then the field is valid. Otherwise, it's invalid.
Run the server! If you enter one word in the Full Name field you'll see an error.
Run the server
Conclusion
Forms are an integral part of how users interact with our websites and web applications. Validating the data the user passes through the form is a critical aspect of our jobs as web developers. However, it doesn’t have to be a difficult process. In this article, you learned how to use Formik and Yup in React. You can use these two libraries to create forms, validate them, and handle their submission. Using these two libraries makes creating forms in React easier and less stressful.