23
React form validation with Formik and Yup
Validating user input on forms prior to submission is one of the most important and fundamental tasks on a website. And thank god we have lots of libraries that help us in this process, however the choice of them comes with the preference of each one. However today I'm going to talk about how to use Formik to verify the data entered by the user and we're going to use Yup to define our schemas.
In this article I'm going to use Yup but you can use any other, like Joi or even Zod for example (if you don't know it and you're a TypeScript programmer, you'll just love it).
I think I can openly say that out of all the form validation libraries for React applications, Formik is by far the most popular. And one of its great advantages is the amount of resources we have available on the most diverse platforms for us to learn.
And to be honest, I wasn't a big fan of the library in the past, but for the past few years I've been using it daily and now I just love it.
As I always do, let's have a small example where I initially have a simple form and then we'll implement Formik.
And that the end result looks like this:
First let's install the following dependencies:
npm install formik yup
Now let's pretend this is your form:
import React from "react";
const App = () => {
return (
<form >
<input
placeholder="Type your First Name"
/>
<small>First Name Error</small>
<input
placeholder="Type your Last Name"
/>
<small>Last Name Error</small>
<button type="submit">
Submit
</button>
</form>
);
};
export default App;
Now let's import Formik and Yup into our project:
import React from "react";
import { useFormik } from "formik";
import * as yup from "yup";
// ...
Now let's create our schema. As you may have noticed, we have two inputs, both of which are going to be strings, so our schema has to have two corresponding properties.
import React from "react";
import { useFormik } from "formik";
import * as yup from "yup";
const schema = yup.object().shape({
firstName: yup.string().min(3).required(),
lastName: yup.string().min(3).required(),
});
// ...
Now let's use the useFormik hook to set the initial values, validationSchema and onSubmit.
const App = () => {
const formik = useFormik({
initialValues: {
firstName: "",
lastName: "",
},
validationSchema: schema,
onSubmit: handleOnSubmit,
});
// ...
};
Now let's create our handleOnSubmit function, in this function you can do whatever you want with the values that come from the form. From storing data in localstorage, to making requests to an API. In this example I'm going to do simple data logging.
const App = () => {
const handleOnSubmit = (values) => {
const fullName = Object.keys(values)
.map((key) => values[key])
.join(" ");
alert(`Hello ${fullName}!`);
};
const formik = useFormik({
initialValues: {
firstName: "",
lastName: "",
},
validationSchema: schema,
onSubmit: handleOnSubmit,
});
// ...
};
Now we need to create the function that will be responsible for handling the form's values and storing them in the inititalValues. To do so, we will import the useCallback hook from React.
import React, { useCallback } from "react";
// ...
const App = () => {
// ...
const setInputValue = useCallback(
(key, value) =>
formik.setValues({
...formik.values,
[key]: value,
}),
[formik]
);
return (
// ...
);
};
Now in our jsx we can make the following changes:
const App = () => {
// ...
return (
<form onSubmit={formik.handleSubmit}>
<input
placeholder="Type your First Name"
value={formik.values.firstName}
onChange={(e) => setInputValue("firstName", e.target.value)}
/>
<small>{formik.errors.firstName}</small>
<input
placeholder="Type your Last Name"
value={formik.values.lastName}
onChange={(e) => setInputValue("lastName", e.target.value)}
/>
<small>{formik.errors.lastName}</small>
{!!formik.errors.lastName && <br />}
<button type="submit" disabled={!formik.isValid}>
Submit
</button>
</form>
);
};
The final code should look like the following:
import React, { useCallback } from "react";
import { useFormik } from "formik";
import * as yup from "yup";
const schema = yup.object().shape({
firstName: yup.string().min(3).required(),
lastName: yup.string().min(3).required(),
});
const App = () => {
const handleOnSubmit = (values) => {
const fullName = Object.keys(values)
.map((key) => values[key])
.join(" ");
alert(`Hello ${fullName}!`);
};
const formik = useFormik({
initialValues: {
firstName: "",
lastName: "",
},
validationSchema: schema,
onSubmit: handleOnSubmit,
});
const setInputValue = useCallback(
(key, value) =>
formik.setValues({
...formik.values,
[key]: value,
}),
[formik]
);
return (
<form onSubmit={formik.handleSubmit}>
<input
placeholder="Type your First Name"
value={formik.values.firstName}
onChange={(e) => setInputValue("firstName", e.target.value)}
/>
<small>{formik.errors.firstName}</small>
<input
placeholder="Type your Last Name"
value={formik.values.lastName}
onChange={(e) => setInputValue("lastName", e.target.value)}
/>
<small>{formik.errors.lastName}</small>
{!!formik.errors.lastName && <br />}
<button type="submit" disabled={!formik.isValid}>
Submit
</button>
</form>
);
};
export default App;
As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. 🧑🏻💻
Hope you have a great day! 🤙
23