Create a login screen in less than 5 minutes with Formik and Yup

March 06, 2019Gavin Williams5 min read

thumbnail

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)
G

Gavin Williams

Web Developer at Theodo