DEV Community

Esther Adebayo
Esther Adebayo

Posted on

How to build a simple form with validation using yup and formik (beginner friendly)

Just imagine the frustration that may come from filling a poorly validated form :( You'll probably bounce off the page. On surface level, forms are very simple to build, but when it comes to validating them, it may become a bit of a challenge.

I tried to build a form with validations a few weeks back and I struggled a bit with it. When I eventually found my way, I realised it'll be great to write a post about it because a lot more people may be facing this same challenge.

Today, I'll be working you through how I built a form with these fields:

Name

Age

Email

Password

Confirm Password

**Button disabled till all validations are met

This Post would be divided into 3 parts

  1. Building the form
  2. Building the validation
  3. Clicking the submit button should take users to a welcome page

Part One

Let's start off with creating the form in React

Creating the form without breaking the form fields into components

import React from "react";

function Form() {
  return (
    <form>
      <div>
        <label htmlFor="name">Name</label>
        <input type="text" name="name" id="name" placeholder="Please Enter your name" />
      </div>
      <div>
        <label htmlFor="age">Age</label>
        <input type="number" name="age" id="age" placeholder="Please Enter your age" />
      </div>
      <div>
        <label htmlFor="email">Email</label>
        <input type="email" name="age" id="email" placeholder="Please Enter your email" />
      </div>
      <div>
        <label htmlFor="password">Password</label>
        <input
          type="password"
          name="password"
          id="password"
          placeholder="Please Enter your password"
        />
      </div>
      <div>
        <label htmlFor="confirm-password">Confirm Password</label>
        <input
          type="password"
          name="confirm-password"
          id="confirm-password"
          placeholder="Please Confirm your password"
        />
      </div>
      <button>Submit</button>
    </form>
  );
}

export default Form;
Enter fullscreen mode Exit fullscreen mode

It'll look this way

Alt Text

To reduce code repetition, let us create a form field component that takes: labelName, name, type and placeholder as Props.

The Form Field Component would look like this:

import React from "react";

function FormField({ name, label, ...rest }) {
  return (
    <div >
      <label htmlFor={name}>{label}</label>
      <input  id={name} name={name} {...rest} />
    </div>
  );
}

export default FormField;

Enter fullscreen mode Exit fullscreen mode

Refactoring our Form Component would give:

import React from "react";
import FormField from "./FormField";

function Form() {
  return (
    <form>
      <FormField
        label="Name"
        type="text"
        name="name"
        placeholder="Please Enter your name"
      />
      <FormField
        label="Age"
        type="number"
        name="age"
        placeholder="Please Enter your age"
      />
      <FormField
        label="Email"
        type="email"
        name="email"
        placeholder="Please Enter your email"
      />
      <FormField
        label="Password"
        type="password"
        name="password"
        placeholder="Please Enter your password"
      />
      <FormField
        label="Confirm Password"
        type="password"
        name="confirm-password"
        placeholder="Please Confirm your password"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

export default Form;
Enter fullscreen mode Exit fullscreen mode

Part Two

Validations for this form are below:

  1. Name: Name must not be less that 3 characters
  2. Email: must be a valid email address
  3. Age: Must be at least 18 years and at most 60 years
  4. Password: Must contain at least one uppercase character, one number, special character and not shorter than 8 characters
  5. Confirm Password: Must match password field

To get started, we would need to install and import 2 libraries into our app.

  1. Yup: Yup is a JavaScript schema builder for value parsing and validation. https://www.npmjs.com/package/yup
  2. Formik: Formik is a library that helps you manage state in forms, handle validation, error messages and form submisiion. https://jaredpalmer.com/formik/docs/overview

Next, we would create initial values for our form fields

const initialValues = {
  name: "",
  age: "",
  email: "",
  password: "",
  confirmPassword: ""
};
Enter fullscreen mode Exit fullscreen mode

After this, we then create our validation schema object using yup

const validationSchema = yup.object().shape({
  name: yup
    .string()
    .required("Name is a required field")
    .min(3, "Name must be at least 3 characters"),
  age: yup
    .number()
    .required("Please supply your age")
    .min(18, "You must be at least 18 years")
    .max(60, "You must be at most 60 years"),
  email: yup
    .string()
    .email()
    .required("Email is a required field"),
  password: yup
    .string()
    .required("Please enter your password")
    .matches(
      /^.*(?=.{8,})((?=.*[!@#$%^&*()\-_=+{};:,<.>]){1})(?=.*\d)((?=.*[a-z]){1})((?=.*[A-Z]){1}).*$/,
      "Password must contain at least 8 characters, one uppercase, one number and one special case character"
    ),
  confirmPassword: yup
    .string()
    .required("Please confirm your password")
    .when("password", {
      is: password => (password && password.length > 0 ? true : false),
      then: yup.string().oneOf([yup.ref("password")], "Password doesn't match")
    })
});
Enter fullscreen mode Exit fullscreen mode

Bringing it all together into the Form Component gives

import React from "react";
import { useFormik } from "formik";
import * as yup from "yup";
import FormField from "./FormField";


//setting the initial values
const initialValues = {
  name: "",
  age: "",
  email: "",
  password: "",
  confirmPassword: ""
};

//creating the validation schema
const validationSchema = yup.object().shape({
  name: yup
    .string()
    .required("A name is required")
    .min(2, "Name must be at least 2 characters"),
  age: yup
    .number()
    .required("Please supply your age")
    .min(18, "You must be at least 18 years")
    .max(60, "You must be at most 60 years"),
  email: yup
    .string()
    .email()
    .required("Email is a required field"),
  password: yup
    .string()
    .required("Please enter your password")
    .matches(
      /^.*(?=.{8,})((?=.*[!@#$%^&*()\-_=+{};:,<.>]){1})(?=.*\d)((?=.*[a-z]){1})((?=.*[A-Z]){1}).*$/,
      "Password must contain at least 8 characters, one uppercase, one number and one special case character"
    ),
  confirmPassword: yup
    .string()
    .required("Please confirm your password")
    .when("password", {
      is: password => (password && password.length > 0 ? true : false),
      then: yup.string().oneOf([yup.ref("password")], "Password doesn't match")
    })
});

function Form({ onSubmit }) {
  //using useFormik 
  const formik = useFormik({
    initialValues,
    validationSchema,
    onSubmit
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <FormField
        label="Name"
        type="text"
        name="name"
        placeholder="Please Enter your name"
      />
      <FormField
        label="Age"
        type="number"
        name="age"
        placeholder="Please Enter your age"
      />
      <FormField
        label="Email"
        type="email"
        name="email"
        placeholder="Please Enter your email"
      />
      <FormField
        label="Password"
        type="password"
        name="password"
        placeholder="Please Enter your password"
      />
      <FormField
        label="Confirm Password"
        type="password"
        name="confirm-password"
        placeholder="Please Confirm your password"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

export default Form;
Enter fullscreen mode Exit fullscreen mode

** Note that we passed the onSubmit callback into the useFormik hook and also passed onSubmit={formik.handleSubmit} into form.

At this point, our task is almost completed, we only need to utilise a few more props and ensure that the error messages show up

We're going to be making use of getFieldProps.

  const nameProps = formik.getFieldProps("name");
  const ageProps = formik.getFieldProps("age");
  const emailProps = formik.getFieldProps("email");
  const passwordProps = formik.getFieldProps('password');
  const confirmPasswordProps = formik.getFieldProps('confirmPassword');
Enter fullscreen mode Exit fullscreen mode

Lastly, we would need to show error messages when the validation isn't met. For example, for the name field, using formik would be

{formik.touched.name && formik.errors.name ? (
        <div>{formik.errors.name}</div>
      ) : null}
Enter fullscreen mode Exit fullscreen mode

The final code for this form would be

import React from "react";
import { useFormik } from "formik";
import * as yup from "yup";
import FormField from "./FormField";

//setting the initial values
const initialValues = {
  name: "",
  age: "",
  email: "",
  password: "",
  confirmPassword: ""
};

//creating the validation schema
const validationSchema = yup.object().shape({
  name: yup
    .string()
    .required("A name is required")
    .min(2, "Name must be at least 2 characters"),
  age: yup
    .number()
    .required("Please supply your age")
    .min(18, "You must be at least 18 years")
    .max(60, "You must be at most 60 years"),
  email: yup
    .string()
    .email()
    .required("Email is a required field"),
  password: yup
    .string()
    .required("Please enter your password")
    .matches(
      /^.*(?=.{8,})((?=.*[!@#$%^&*()\-_=+{};:,<.>]){1})(?=.*\d)((?=.*[a-z]){1})((?=.*[A-Z]){1}).*$/,
      "Password must contain at least 8 characters, one uppercase, one number and one special case character"
    ),
  confirmPassword: yup
    .string()
    .required("Please confirm your password")
    .when("password", {
      is: password => (password && password.length > 0 ? true : false),
      then: yup.string().oneOf([yup.ref("password")], "Password doesn't match")
    })
});

function Form({ onSubmit }) {
  //using useFormik
  const formik = useFormik({
    initialValues,
    validationSchema,
    onSubmit
  });

  //use formik.getFieldProps for input fields
  const nameProps = formik.getFieldProps("name");
  const ageProps = formik.getFieldProps("age");
  const emailProps = formik.getFieldProps("email");
  const passwordProps = formik.getFieldProps("password");
  const confirmPasswordProps = formik.getFieldProps("confirmPassword");

  /**
   * getFieldProps is a way to reduce boilerplate (repetitive) code.
   * It returns helper methods like `onChange`, `onBlur`, `value`, `name`.
   *
   * @see Formik https://jaredpalmer.com/formik/docs/tutorial#getfieldprops
   */
  return (
    <form onSubmit={formik.handleSubmit}>
      <FormField
        label="Name"
        type="text"
        placeholder="Please Enter your name"
        {...nameProps}
      />
      {formik.touched.name && formik.errors.name ? (
        <div>{formik.errors.name}</div>
      ) : null}
      <FormField
        label="Age"
        type="number"
        {...ageProps}
        placeholder="Please Enter your age"
      />
      {formik.touched.age && formik.errors.age ? (
        <div>{formik.errors.age}</div>
      ) : null}
      <FormField
        label="Email"
        type="email"
        placeholder="Please Enter your email"
        {...emailProps}
      />
      {formik.touched.email && formik.errors.email ? (
        <div>{formik.errors.email}</div>
      ) : null}
      <FormField
        label="Password"
        type="password"
        placeholder="Please Enter your password"
        {...passwordProps}
      />
      {formik.touched.password && formik.errors.password ? (
        <div>{formik.errors.password}</div>
      ) : null}
      <FormField
        label="Confirm Password"
        type="password"
        placeholder="Please Confirm your password"
        {...confirmPasswordProps}
      />
      {formik.touched.confirmPassword && formik.errors.confirmPassword ? (
        <div>{formik.errors.confirmPassword}</div>
      ) : null}
      <button type="submit" disabled={!(formik.isValid && formik.dirty)}>Submit</button>
    </form>
  );
}

export default Form;

Enter fullscreen mode Exit fullscreen mode

Note that to disable the button till all form validations are met, I only passed: disabled={!(formik.isValid && formik.dirty)} as prop into the button.

Part 3

As with every form, after clicking the submit button, you want users to go to another page. I'll show you how exactly to do that.

(Just in case you'll need further explanation on routing, in my next blog post, I'll take you step by step on how to set up a routing in react).

For now, all you need to do is:

  1. Install "react-router-dom"
  2. Create the component or page you'll want users to see after submission of the form. In my case, I'll create a Welcome page
import React from "react";

function Welcome() {
  return (
    <div>
      <h3>Hello and welcome</h3>
    </div>
  );
}

export default Welcome;
Enter fullscreen mode Exit fullscreen mode

In App put this:

import React from "react";
import { Route, BrowserRouter as Router, Switch } from "react-router-dom";
import Form from "./Form";
import Welcome from "./Welcome";

export default function App() {
  return (
    <Router>
      <Switch>
        <Route
          exact
          path="/"
          render={props => (
            <Form
              onSubmit={value => {
                props.history.push("/welcome");
              }}
            />
          )}
        />
        <Route exact path="/welcome" component={Welcome} />
      </Switch>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

Congrats, you just completed this simple tutorial.

I hope this has been really helpful in making you understand how to build a form with validation from scratch using yup and formik.

Kindly leave a comment if you found this useful and check out my other posts.

Top comments (9)

Collapse
 
ronyfr3 profile image
ronyfr3

nice blog

Collapse
 
_estheradebayo profile image
Esther Adebayo

Thank you

Collapse
 
masthanvalismd profile image
MOHAMMED MASTHAN VALI

very helpful for a beginner thank you so much!

Collapse
 
hemantgovekar profile image
Hemant Govekar

This was an awesome article.

Collapse
 
rogerswandera profile image
Rogers-Wandera

It was so helpfull for such a younger developer like me

Collapse
 
_estheradebayo profile image
Esther Adebayo

Amazing! Glad to hear that.

Collapse
 
angelacpd profile image
Angela

This is a great tutorial! Thank you!

Collapse
 
_estheradebayo profile image
Esther Adebayo

Thank you so much

Collapse
 
fideltodayy profile image
Fidel Otieno

This is Amazing Esther, thank you for this.
I think we should acknowledge that in the current react version(react version 6), switch has been updated to Routes?
Just incase anyone is stuck on that.