Create a login screen in less than 5 minutes with Formik and Yup
March 06, 2019Gavin Williams4 min read

In this article, you will quickly learn how to build a simple login form using Formik and Yup. For those who don’t know, Formik is a relatively new but already widely-used form library for React. As a much less complex alternative to Redux Form, it keeps your React forms far simpler than and more painless than other React form libraries. There are three main goals Formik tries to achieve:
-
Managing form state
-
Form Validation and error messages
-
Handling form submission
We will be covering all three of the topics in this guide, with a particular emphasis on validation. The validation will be done using Yup a separate library specifically for validation which Formik works with implicitly.
Setup
Formik can be easily installed with both yarn and npm
$ yarn add formik
Or
$ npm install formik
Your components can then be imported easily using
import { Form, Field } from “formik”
We will also be using yup later in this tutorial for validation, which can also be imported using both yarn and npm
$ yarn add yup
Or
$ npm install yup
Yup can be imported using the following
import * as yup from “yup”
The withFormik Higher Order Component
In this tutorial, we will use the withFormik higher order component.
The withFormik higher order component passes props and handler functions into your React component. All Formik forms need to be passed a handleSubmit prop. This should be a function which is called whenever the form is submitted. The withFormik wrapper automatically handles the onChange and onBlur functionality for you, however, this can be customised if needed.
export default withFormik({
// Handles our submission
handleSubmit: (values, { setSubmitting }) => {
// This is where you could send the submitted values to the backend
console.log("Submitted Email:", values.email)
console.log("Submitted Password:", values.password)
// Simulates the delay of a real request
setTimeout(() => setSubmitting(false), 3 * 1000)
},
validationSchema: LoginValidation,
})(LoginWrapper)
The Field Component
The field component is the main component used for each input on your form. The field uses the name attribute to connect it to the Formik state. A simple example of a field:
<Field type="text" name="email" placeholder="email" />
The onChange and onBlur are passed in automatically by the withFormik higher order component and the value is set in the Formik state. By default the Field uses the HTML component. Other types of inputs can be used by providing the component prop.
component="select"
The full Form
The fields can be wrapped in the Formik Form component, which is a wrapper around the HTML . This automatically hooks into Formiks handleSubmit and handleReset functions.
<Form>
<Field type="text" name="email" placeholder="email" />
<Field type="text" name="password" placeholder="password" />
<button type="submit">Submit</button>
</Form>
Pressing the submit button in this example will trigger the handleSubmit function in the withFormik higher order component.
Yup Validation
As mentioned before, Yup is a separate library for Validation which works implicitly with Formik. Yup uses an object validation schema to validate the forms. Yup can allow you to do very powerful validation easily with its schema.
const LoginValidation = yup.object().shape({
email: yup
.string()
.email()
.required(),
password: yup
.string()
.min(8)
.max(16)
.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]$")
.required(),
})
This is the example validation schema for the login form. In the example, the email field is required and uses the inbuilt Yup validation for checking if the field contents is a valid email. For the password field, we have set that it must be between 8 and 16 characters long and have provided a regex string which checks that it contains at least one lowercase letter, one uppercase letter and a number. More information about Yup and the full API can be found here.
Yup can be used easily with Formik using a specialist prop for Yup validation schema called validationSchema. This can be passed into the withFormik higher order component.
validationSchema: LoginValidation
Displaying Errors
Errors can easily be handled with a custom Formik component called ErrorMessage. In Formik every error generated by the validation schema is stored in a prop called errors. The ErrorMessage component checks if there are any errors with the same name as the field and checks if the field has been touched and displays any relevant errors. This can be added to the simple login form as follows.
<Form>
<Field type="text" name="email" placeholder="email" />
<ErrorMessage name="email" />
<Field type="text" name="password" placeholder="password" />
<ErrorMessage name="password" />
<button type="submit">Submit</button>
</Form>
Summary
Hopefully, this guide has shown you how powerful Formik can be but also how easy it is to use and how painless it can be to implement some pretty powerful logic. Further information can be found in the Formik Documentation. A full overview of the code used in this tutorial can be found below.
import React, { PureComponent } from "react"
import { Form, Field, ErrorMessage } from "formik"
export default class Login extends PureComponent {
render() {
return (
<Form>
<Field type="text" name="email" placeholder="email" />
<ErrorMessage name="email" />
<Field type="text" name="password" placeholder="password" />
<ErrorMessage name="password" />
<button type="submit"> Submit </button>
</Form>
)
}
}
import { withFormik } from "formik"
import * as yup from "yup"
import Login from "./Login"
const LoginWrapper = Login
const LoginValidation = yup.object().shape({
email: yup
.string()
.email()
.required(),
password: yup
.string()
.min(8)
.max(16)
.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]$")
.required(),
})
export default withFormik({
// Handles our submission
handleSubmit: (values, { setSubmitting }) => {
// This is where you could send the submitted values to the backend
console.log("Submitted Email:", values.email)
console.log("Submitted Password:", values.password)
// Simulates the delay of a real request
setTimeout(() => setSubmitting(false), 3 * 1000)
},
validationSchema: LoginValidation,
})(LoginWrapper)