3 Ways to Build React Forms with Formik Pt.1
Tags: webdev, react, tutorial, javascript
Published: 2021-09-20 07:36:43

Formik is one of the most popular libraries for building forms. It helps developers do a lot of things with much few lines of code. Some of these things are form state management, validation and error handling. This tutorial will show you three ways in which you can use formik to build React forms.

## The pain of building forms

Building React forms may sound easy, but it is not. There are a lot of things involved. Usually the easiest part is to put together the form. Then come the hard parts. There is a form state management and handling values filled in the form. This also includes preparing the initial state of the form.

When you have this, you need functions to handle those fields to keep the form state up-to-date. Next comes validation. You have to ensure all those values are really legit, or valid. This may require some validation logic including regex. As a part of the validation, you also have to ensure you have valid values for all required fields.

What if some value is invalid, or required and missing? When this happens, you have to find out what value is invalid and display correct error message for that field. That's not the end. You also have to ensure that when the value is valid the error message disappears. Only when you have all this covered you can proceed to submitting the form.

## Building React forms with Formik

If this all sounds like a pain to you, you are not alone. For one of these React developer this pain was a motivation to come up with a solution. This solution he came up with was [Formik]. The idea is to make building React forms easier for developers by doing most of the heavy lifting for them.

This includes the usual things involved in building forms, the form state management, input validation, showing error messages when necessary and also handling form submission. At this moment, there are at least three ways to use Formik to build forms. Below, we will take a look at each of these ways.

## Creating simple form validation schema

Formik supports multiple ways to validate forms. One way is to write the validation logic by yourself. Formik will then use that logic and handle error messages. Another option is to use some validation library. In this tutorial, we will choose the second option and use validation library called [Yup].

What this library does is it helps you create validation schema for your form. This schema is basically an object that contains validation rules for individual fields in your form. Formik as able to use this schema, built with `Yup`, to validate all fields defined in the schema against their specific rules.

Another nice feature of `Yup` schema is the option to specify error message for each rule. You do this by passing some text as an argument to rules functions. For example, when field is required, you use `required()` function. To specify the error message for this rule you pass the message as an argument: `required('This field is required.')`.

All forms we will use in this tutorial will have three fields: name, email and password. All these fields will be required. We will specify all this in the form schema created with `Yup`. For the email field, we will also specify that it has to match email format.

```jsx
import * as Yup from 'yup'

const formSchema = Yup.object().shape({
  name: Yup.string().required('First name is required'),
  email: Yup.string().email('Invalid email').required('Email is required'),
  password: Yup.string().required('Password is required'),
})
```

## Custom forms with Formik

The first option is to use Formik as a wrapper for your React forms. You will create the form and components for all fields you need. This also includes error messages for those fields. Formik will take care of form state, validation and error handling. This option will require only one component provided by Formik called `Formik`.

### The Formik component

This `Formik` component will serve as a wrapper for the whole form. This doesn't mean it will replace the `form` element wrapping your form. If you are using the `form` element, it will stay. The `Formik` component will wrap this element as well. The `Formik` component has a couple of attributes that will be handy.

These attributes are `initialValues`, `onSubmit` and `validationSchema`. The `initialValues` attribute specifies object to define initial values of all fields and creates form state. The `onSubmit` attribute allows you to specify handler function for `onSubmit` event. The `validationSchema` attribute allows to specify validation schema to use.

Initial values for all fields, name, email and password, will be empty strings. For now, to handle `onSubmit` event, we will now use [arrow function] and simple `console.log()` to log submitted values. `Formik` component uses [render-prop pattern] that helps share code between React component. Don't worry you don't need to know how this works.

All you need to know is that `Formik` expects its direct children to be a function that returns some React component. In this case, that returned component will be the `form` element and its content. Because we will work with custom form elements, we will need to expose some data from `Formik` component so we can work with them.

We can get this data by using [object destructuring] in the function that returns the `form` element. The data we will need are `values`, `errors`, `touched`, `handleBlur`, `handleChange` and `handleSubmit`. The `values` is an object that contains current values for each form field. We will use this to specify values for input `value` attribute.

The `errors` is also an object. If there are any errors in the form, invalid or missing fields, you will find it inside this object. The `touched` is an object that tells which form field has been touched and which not. Touched field means that someone interacted with that field, it was focused.

The `handleBlur` and `handleChange` are handlers for inputs `onBlur` and `onChange` events. These two events will allow Formik track changes in values, update form state, update "touched" status and also run validations when fields lose focus. The `handleSubmit` is a handler for form `onSubmit` event.

We will use this handler for `onSubmit` attribute of `form` element to trigger Formik's `handleSubmit` function when the form is submitted.

```jsx
// Import dependencies:
import { memo } from 'react'
import { Formik } from 'formik'
import * as Yup from 'yup'

// Create form validation schema:
const formSchema = Yup.object().shape({
  name: Yup.string().required('First name is required'),
  email: Yup.string().email('Invalid email').required('Email is required'),
  password: Yup.string().required('Password is required'),
})

// Create the form component:
export const FormCustom = memo(() => {
  return (
    <Formik
      initialValues={{ name: '', email: '', password: '' }}
      onSubmit={(values) => {
        console.log(values)
      }}
      validationSchema={formSchema}
    >
      {({
        values,
        errors,
        touched,
        handleBlur,
        handleChange,
        handleSubmit,
      }) => <form></form>}
    </Formik>
  )
})

FormCustom.displayName = 'FormCustom'
```

### The form content

The next step is putting together the content of the form, individual fields. This will be quick and easy. Each field will be composed of `div` element used as a wrapper. Inside this wrapper will be `label` and `input` elements. There will be also `p` element with an error message. To make sure everything works we will need two things.

First, we will need to use correct value for input `name` attributes. Formik uses this attribute, or `id`, to connect each field with correct property in the form state. We set the initial values object to have properties `name`, `email` and `password`. This means that we will have to use the same values for each `name` attribute, or `id`, or both.

The second thing are the `onChange` and `onBlur` input events handler functions. We need to connect Formik state with each input. This will allow Formik to track changes of values and blur events and update `values`, `touched` and `errors` accordingly. Last thing are the `value` input attributes for each input field.

Those inputs should be controlled by Formik state. This will allow to display current values in Formik state as values of corresponding input fields. To do this, we will use `values` object and its specific property to get latest correct value for each input field.

```jsx
// ... Previous code
export const FormCustom = memo(() => {
  return (
    <Formik
      initialValues={{ name: '', email: '', password: '' }}
      onSubmit={(values) => {
        console.log(values)
      }}
      validationSchema={formSchema}
    >
      {({
        values,
        errors,
        touched,
        handleBlur,
        handleChange,
        handleSubmit,
      }) => (
        <form onSubmit={handleSubmit} noValidate>
          <div>
            <label htmlFor="name">Name</label>
            <input
              type="text"
              name="name"
              value={values.name}
              onChange={handleChange}
              onBlur={handleBlur}
            />
          </div>

          <div>
            <label htmlFor="email">Email</label>
            <input
              type="email"
              name="email"
              value={values.email}
              onChange={handleChange}
              onBlur={handleBlur}
            />
          </div>

          <div>
            <label htmlFor="password">Password</label>
            <input
              type="password"
              name="password"
              value={values.password}
              onChange={handleChange}
              onBlur={handleBlur}
            />
          </div>

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

FormCustom.displayName = 'FormCustom'
```

### Simple error messages

We have Formik and form state. We also have form content. The last thing that remains are error messages. One part of this is already covered by validation schema we created with `Yup` and Formik. The second part are error messages. We have to tell Formik where we want to show them and under what condition. Both these things will be easy.

To address the first, we will put each error message under corresponding field. We will use `p` elements to show the text we defined for each rule in our `Yup` validation schema. The condition for when to display each message will following: We want to show the error when field is empty or invalid, but only after it has been touched.

This will help prevent the errors popping up when someone just opens the form. And, don't worry. Formik automatically sets all fields to touched when the form is submitted. So, if someone tries to submit empty form, error messages for invalid fields will correctly pop up because Formik will set all fields as touched and there are some errors.

Since we specified error messages in validation schema we only have to ensure that Formik displays correct error message for each field. We will do this by using the `errors` object and correct property (field name). We will use the same property with `touched` object to check if specific field has been touched.

```jsx
// ... Previous code
export const FormCustom = memo(() => {
  return (
    <Formik
      initialValues={{ name: '', email: '', password: '' }}
      onSubmit={(values) => {
        console.log(values)
      }}
      validationSchema={formSchema}
    >
      {({
        values,
        errors,
        touched,
        handleBlur,
        handleChange,
        handleSubmit,
      }) => (
        <form onSubmit={handleSubmit} noValidate>
          <div>
            <label htmlFor="name">Name</label>
            <input
              type="text"
              name="name"
              value={values.name}
              onChange={handleChange}
              onBlur={handleBlur}
            />
            {/* Add error message for "Name" field */}
            {errors.name && touched.name && <p>{errors.name}</p>}
          </div>

          <div>
            <label htmlFor="email">Email</label>
            <input
              type="email"
              name="email"
              value={values.email}
              onChange={handleChange}
              onBlur={handleBlur}
            />

            {/* Add error message for "Email" field */}
            {errors.email && touched.email && <p>{errors.email}</p>}
          </div>

          <div>
            <label htmlFor="password">Password</label>
            <input
              type="password"
              name="password"
              value={values.password}
              onChange={handleChange}
              onBlur={handleBlur}
            />

            {/* Add error message for "Password" field */}
            {errors.password && touched.password && <p>{errors.password}</p>}
          </div>

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

FormCustom.displayName = 'FormCustom'
```

### Putting it together

Since Formik, form content and error messages are done your custom React form is complete as well. Below is the complete code for the whole form. The last thing that needs some work is what to do when the form is submitted. You handle this in Formik's `onSubmit` attribute and its handler function. Replace that `console.log()` with anything you need.

```jsx
// Import dependencies:
import { memo } from 'react'
import { Formik } from 'formik'
import * as Yup from 'yup'

// Create form validation schema:
const formSchema = Yup.object().shape({
  name: Yup.string().required('First name is required'),
  email: Yup.string().email('Invalid email').required('Email is required'),
  password: Yup.string().required('Password is required'),
})

// Create the form component:
export const FormCustom = memo(() => {
  return (
    <Formik
      initialValues={{ name: '', email: '', password: '' }}
      onSubmit={(values) => {
        console.log(values)
      }}
      validationSchema={formSchema}
    >
      {({
        values,
        errors,
        touched,
        handleBlur,
        handleChange,
        handleSubmit,
      }) => (
        <form onSubmit={handleSubmit} noValidate>
          <div>
            <label htmlFor="name">Name</label>
            <input
              type="text"
              name="name"
              value={values.name}
              onChange={handleChange}
              onBlur={handleBlur}
            />
            {errors.name && touched.name && <p>{errors.name}</p>}
          </div>

          <div>
            <label htmlFor="email">Email</label>
            <input
              type="email"
              name="email"
              value={values.email}
              onChange={handleChange}
              onBlur={handleBlur}
            />
            {errors.email && touched.email && <p>{errors.email}</p>}
          </div>

          <div>
            <label htmlFor="password">Password</label>
            <input
              type="password"
              name="password"
              value={values.password}
              onChange={handleChange}
              onBlur={handleBlur}
            />
            {errors.password && touched.password && <p>{errors.password}</p>}
          </div>

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

FormCustom.displayName = 'FormCustom'
```

## Conclusion: 3 Ways to build React forms with Formik pt.1

Formik library makes it much easier to build and work with React forms. This tutorial showed you how to make the `Formik` component to work with custom HTML elements. This can help you use `Formik` to handle only things such as state management and validation while letting you do the rest as you want.

[formik]: https://formik.org
[yup]: https://www.npmjs.com/package/yup
[arrow function]: https://blog.alexdevero.com/javascript-arrow-functions/
[render-prop pattern]: https://reactjs.org/docs/render-props.html
[object destructuring]: https://blog.alexdevero.com/destructuring-assignment-javascript/#destructuring-objects