TutorialsCourses
Course Menu
Formik for Beginners

Building a Login Form

Introduction

One form you will likely need to build is a login form. Accepting an email, password can seem simple but there are a lot of complicated interactions to tackle. Validating the minimum length of a password, validating emails, preventing submission during errors, displaying errors correctly. There are many things that Formik will handle and step out of the way and allow you to build a login form with ease.

Setting up Validation

We'll use yup and all we will need form yup is object and string.

import { object, string } from "yup";

For our login validation we'll use the structure of email and password.

Looking at email structure. We'll first say it's required. Then also say that the structure needs to match email. Which will validate that it's a proper email structure. We can change the message we show to the user based upon if it's empty we'll say Required, and if it's email we'll add a more specific message.

string().required("Required").email("Valid email required");

Now looking at password structure it will need to be a string. With a minimum of 8 characters and also required.

string().min(8, "Required").required("Required");

Bringing it all together we have our Login validation which we use object to create a shape of our data and use our validations to apply to email field and our password field.

const LoginValidation = object().shape({
  email: string().required("Required").email("Valid email required"),
  password: string().min(8, "Required").required("Required"),
});

Setting up the Form

Now we need to setup our form. There is a lot of styling going on here which we will ignore.

The key features here we are setting up our initial values for email and password. Passing our Formik component the onSubmit function, and finally passing our LoginValidation to the validationSchema.

Our submit button is type="submit" which when pressed will touch all fields, and trigger our validation. If all validation passes then the onSubmit function will be called. Any errors and it will not be called.

const handleSubmit = (values) => {
  console.log(values);
};

return (
  <div className="h-screen flex items-center justify-center flex-col bg-gray-100">
    <Formik
      initialValues={{
        email: "",
        password: "",
      }}
      onSubmit={handleSubmit}
      validationSchema={LoginValidation}
    >
      {() => {
        return (
          <Form className="bg-white w-6/12 shadow-md rounded px-8 pt-6 pb-8">
            <div className="flex items-center justify-between">
              <button
                className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                type="submit"
              >
                Sign In
              </button>
            </div>
          </Form>
        );
      }}
    </Formik>
  </div>
);

Building the Input

Our input will take advantage of useField. We will accept the name for the field as a property, as well as the label for the field to display. The rest will be props that can be spread onto the input.

const Input = ({ name, label, ...props }) => {
  const [field, meta] = useField(name);
  return (
    <div className="mb-4">
      <label className="block text-gray-700 text-sm font-bold" for={field.name}>
        {label}
      </label>
      <input {...field} {...props} />
    </div>
  );
};

For toggling our border when an error happens we use the meta field to check error and touched. This will then set the inputs border color to red so the user knows there is an error.

meta.error && meta.touched ? "border-red-500" : "";

Then we'll add our ErrorMessage and pass the field.name to it. We'll say it's a component="div" so that it will force itself to the next line and then apply class names.

const Input = ({ name, label, ...props }) => {
  const [field, meta] = useField(name);
  return (
    <div className="mb-4">
      <label className="block text-gray-700 text-sm font-bold" for={field.name}>
        {label}
      </label>
      <input
        className={`${
          meta.error && meta.touched ? "border-red-500" : ""
        } shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline`}
        {...field}
        {...props}
      />
      <ErrorMessage
        name={field.name}
        component="div"
        className="text-red-500 text-xs"
      />
    </div>
  );
};

All Together Now

All together the glue code for a Login form is pretty minimal. We declared some validation, passed it to our form, glued an input in using useField, and handling whether or not onSubmit is called automatically. The rest of this is just styling.

import React from "react";
import { Formik, Form, Field, useField, ErrorMessage } from "formik";
import { object, string } from "yup";

const LoginValidation = object().shape({
  email: string().required("Required").email("Valid email required"),
  password: string().min(8, "Required").required("Required"),
});

const Input = ({ name, label, ...props }) => {
  const [field, meta] = useField(name);
  return (
    <div className="mb-4">
      <label className="block text-gray-700 text-sm font-bold" for={field.name}>
        {label}
      </label>
      <input
        className={`${
          meta.error && meta.touched ? "border-red-500" : ""
        } shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline`}
        {...field}
        {...props}
      />
      <ErrorMessage
        name={field.name}
        component="div"
        className="text-red-500 text-xs"
      />
    </div>
  );
};

function App() {
  const handleSubmit = (values) => {
    console.log(values);
  };

  return (
    <div className="h-screen flex items-center justify-center flex-col bg-gray-100">
      <Formik
        initialValues={{
          email: "",
          password: "",
        }}
        onSubmit={handleSubmit}
        validationSchema={LoginValidation}
      >
        {() => {
          return (
            <Form className="bg-white w-6/12 shadow-md rounded px-8 pt-6 pb-8">
              <Input name="email" label="Email" />;
              <Input name="password" label="Password" type="password" />
              <div className="flex items-center justify-between">
                <button
                  className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                  type="submit"
                >
                  Sign In
                </button>
              </div>
            </Form>
          );
        }}
      </Formik>
    </div>
  );
}

export default App;