React Form Management using Formik and Yup
Tags: react, formik, yup, form
Published: 2024-03-01 07:50:21

In the world of web development, creating forms is one of the most common tasks. Yet, building forms in React applications can become quite challenging, especially when it comes to managing large forms with multiple fields and validating user input. Thankfully, libraries like [Formik](https://formik.org/) and [Yup](https://github.com/jquense/yup) come to the rescue, making this process much simpler and more efficient.

In this tutorial, we will explore how you can effortlessly handle form state and input validation using Formik and Yup.

## Prerequisites: Setting Up Your React Application

### Install NodeJS and NPM

Before we dive into the code, ensure that you have installed:

- [Node.js](https://nodejs.org/en)
- [npm (Node Package Manager)](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)

### Create your React app

If you do not have a React application already, let's start with creating one using [Create React App](https://create-react-app.dev/). To do so, open your terminal and run the following commands:

```sh
mkdir my-formik-yup-app
cd my-formik-yup-app
npx create-react-app my-formik-yup-app .
```

### Install formik and yup

Having created the app, the next step is installing the required libraries - i.e. Formik and Yup:

```sh
npm install formik yup
```

### Starting Your App Locally

To start your app locally, run the following command in your terminal:

```sh
  npm start
```

If everything is set up correctly, you should be able to see the default react app page by opening your browser and navigating to **http://localhost:3000**.

## Creating a Form Component

Now that we have the React app running, let's focus on creating the form. For our example, let's start with a simple user registration form with fields for username, email, and password.

### Creating a simple component (for beginners)

Inside the `src/` folder of your React app, create a new file called `RegistrationForm.jsx` and add the following code:

```jsx
import React from "react";

const RegistrationForm = () => {
  return <h1>Registration Form </h1>;
};

export default RegistrationForm;
```

To display the `RegistrationForm` component on the main page of the app, remove all unnecessary components from `App.js` and add the `RegistrationForm` instead:

```jsx
import "./App.css";
import RegistrationForm from "./RegistrationForm";

function App() {
  return (
    <div className="App">
      <RegistrationForm />
    </div>
  );
}

export default App;
```

With this you should be able to see the heading "Registration Form" on your main page.

### Creating the registration form in `RegistrationForm.jsx`

#### Step 1: Create the form skeleton

```jsx
import React from "react";

const RegistrationForm = () => {
  return (
    <div>
      <h1>Simple Registration Form</h1>

      <form>
        <div>
          <label htmlFor="username">Username</label>
          <input type="text" id="username" name="username" />
        </div>

        <div>
          <label htmlFor="email">Email</label>
          <input type="email" id="email" name="email" />
        </div>

        <div>
          <label htmlFor="password">Password</label>
          <input type="password" id="password" name="password" />
        </div>

        <button type="submit">Submit</button>
      </form>
    </div>
  );
};

export default RegistrationForm;
```

#### Step 2: Apply styles (optional)

If you want to style up this form a little bit, you can create a `style.css` file in the same directory and add your styles. Here is some basic form style that I used for this example.

```css
/* ./style.css */

.label {
  text-align: left;
  font-weight: 500;
  font-size: x-small;
  color: rgb(115, 115, 115);
}

.text-field {
  width: 100%;
  height: 33px;
  margin: auto;
  background-color: #fff;
  border-radius: 10px;
  border: 2px solid rgb(202, 202, 202);
}

.text-field:focus {
  border: 2px solid #fe8923;
}
.input-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: self-start;
  width: 70%;
}

.form-container {
  display: flex;
  flex-direction: column;
  justify-items: center;
  align-items: center;
  gap: 15px;
}

.form-card {
  width: 720px;
  margin: 100px auto;
  padding-top: 10px;
  padding-bottom: 30px;

  box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
  border: 1px solid transparent;
  border-radius: 20px;
  background-color: #f2fbff;
}

.button {
  width: 180px;
  background-color: #fe8923;
  color: #fff;
  box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
  border: 2px solid #fe8923;
  border-radius: 10px;
  padding: 10px;
  margin: 30px 0 0 0;
  font-weight: 600;
}

.button:hover {
  background-color: #fff;
  color: #fe8923;
  cursor: pointer;
}
```

Now you have to apply the class names to the elements you would like to style.

```jsx
import React from "react";
import "./style.css";

const RegistrationForm = () => {
  return (
    <div className="form-card">
      <h1>Simple Registration Form</h1>

      <form>
        <div className="form-container">
          <div className="input-container">
            <label htmlFor="username" className="label">
              Username
            </label>
            <input
              type="text"
              id="username"
              name="username"
              className="text-field"
            />
          </div>

          <div className="input-container">
            <label htmlFor="email">Email</label>
            <input
              type="email"
              id="email"
              name="email"
              className="text-field"
            />
          </div>

          <div className="input-container">
            <label htmlFor="password" className="label">
              Password
            </label>
            <input
              type="password"
              id="password"
              name="password"
              className="text-field"
            />
          </div>
        </div>

        <button type="submit" className="button">
          Submit
        </button>
      </form>
    </div>
  );
};

export default RegistrationForm;
```

With this our form skeleton is ready. Let's make it functional in the next steps.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u469d96344w5arvtfhx3.png)

#### Step 3: Use Formik for state management

Firstly, import the formik components for building form elements - `Form`, `Field` and the form wrapper - `Formik`, and replace the `<form>` and `<input>` elements with them.

Secondly, define the initial values for your form fields and pass them to Formik with the `initialValues` prop.

Last but not least, define your submit handler and pass it to Formik with the `onSubmit` prop.

```jsx
import React from "react";
import "./style.css";

import { Formik, Form, Field } from "formik";

const RegistrationForm = () => {
  const initialFormValues = { username: "", email: "", password: "" };
  const submitHandler = (values) => {
    console.log(values);
    // Handle form submission logic
  };

  return (
    <div className="form-card">
      <h1>Simple Registration Form</h1>
      <Formik
        initialValues={initialFormValues}
        onSubmit={(v) => submitHandler(v)}
      >
        <Form>
          <div className="form-container">
            <div className="input-container">
              <label htmlFor="username" className="label">
                Username
              </label>
              <Field
                type="text"
                id="username"
                name="username"
                className="text-field"
              />
            </div>

            <div className="input-container">
              <label htmlFor="email" className="label">
                Email
              </label>
              <Field
                type="email"
                id="email"
                name="email"
                className="text-field"
              />
            </div>

            <div className="input-container">
              <label htmlFor="password" className="label">
                Password
              </label>
              <Field
                type="password"
                id="password"
                name="password"
                className="text-field"
              />
            </div>
          </div>

          <button type="submit" className="button">
            Submit
          </button>
        </Form>
      </Formik>
    </div>
  );
};

export default RegistrationForm;
```

Great, you should be able to edit your input values and see them in the browser console when you click on the submit button.

#### Step 4: Add validation with Yup

Validation with yup is a very easy and straightforward process.

First, you have to define the validation schema you want to apply to the form. In our example we call the schema `registrationValidationSchema` and it has three required fields - username, email and password.

You can check out all the methods you can apply to string values in the [Yup docs](https://github.com/jquense/yup?tab=readme-ov-file#string). We will only use `required` (to validate presence), `email` (to validate email format), and `min` (to validate minimum characters count). As argument to each of those methods, you can pass the message that will be displayed to the user when the user input is invalid.

Once your schema is defined, you can apply it to the form by passing it as a prop - `validationSchema`, to the `Formik` component.

Lastly, to display the validation message to the user, use the `ErrorMessage` component from `formik` for each input field. It is important to pass the `name` prop to `ErrorMessage` in order to display the error for the correct field.

```jsx

import React from 'react';
import "./style.css";

import { Formik, Form, Field, ErrorMessage } from 'formik';
import \* as Yup from 'yup';

const RegistrationForm = () => {

  const initialFormValues = { username: "", email: "", password: "" };

  const registrationValidationSchema = Yup.object({
          username: Yup.string().required("Username is required"), // the message shown when the field is empty
          email: Yup.string()
            .email("Invalid email address") // the message shown when the email input has invalid format
            .required("Email is required"), // the message shown when the field is empty
          password: Yup.string()
            .min(8, "Password must be at least 8 characters") // the message shown when the password input has less than 8 symbols
            .required("Password is required"), // the message shown when the field is empty
        });

  const submitHandler = (values) => console.log(values)

  return (
    <div className="form-card">
      <h1>Simple Registration Form</h1>
      <Formik
        initialValues={initialFormValues}
        validationSchema={registrationValidationSchema} // applis the registration validation shema to the form
        onSubmit={(v)=> submitHandler(v)}
      >
       <Form>
          <div className="form-container">
            <div className="input-container">
              <label htmlFor="username" className="label">
                Username
              </label>
              <Field
                type="text"
                id="username"
                name="username"
                className="text-field"
              />
              <ErrorMessage name="username" component="div" />
            </div>

            <div className="input-container">
              <label htmlFor="email" className="label">
                Email
              </label>
              <Field
                type="email"
                id="email"
                name="email"
                className="text-field"
              />
              <ErrorMessage name="email" component="div" />
            </div>

            <div className="input-container">
              <label htmlFor="password" className="label">
                Password
              </label>
              <Field
                type="password"
                id="password"
                name="password"
                className="text-field"
              />
              <ErrorMessage name="password" component="div" />
            </div>
          </div>

          <button type="submit" className="button">
            Register
          </button>
        </Form>
      </Formik>
    </div>
  );
};

export default RegistrationForm;
```

Congratulations, your React form is ready to use! How easy was that! Far better than using the standard `useState` hook for each input field and implementing additional functions for state validation.

## Conclusion

In this tutorial, we've created a basic registration form in a React app using Formik for form management and Yup for form validation. This amazing combination allows for a powerful yet straightforward approach to handling form state and input validation in React applications. Feel free to extend this example by adding more form fields, customizing validation messages and styling, or integrating backend services for form submission.

Happy coding!