3 Ways to Build React Forms with Formik Pt.2
Tags: webdev, react, tutorial, javascript
Published: 2021-09-27 05:57:30

The Formik library helps to build React forms faster with its state management and component. This tutorial will help you learn how to build a React form using components provided by Formik library. You will also learn how to create a simple validation schema for forms with Yup library.

## A quick intro

This short series is about three ways to build React forms with Formik. In the [first part], we've taken a look at the first way. We focused on build React forms with [Formik] using only the bare essentials Formik provides. In this part, we will lean much more towards Formik and its components.

Generally speaking, we can use Formik to build React forms with two approaches. The first one is with React components. The second is by using Formik hooks. We've already, partially, explored the first approach in the first part. In this part, we will take a look at this approach one more time, now using Formik components instead of custom.

## A word on dependencies

This tutorial will use minimal number of dependencies. First, we will need the `react`, `react-dom` and `react-scrips`. These three will help as get the React app from the ground. You can either install these dependencies by yourself or use the [create-react-app] app to setup everything for you.

When you have the React app ready, you two additional dependencies. The first one will be `Formik`, library that will power our form. The second dependency will be `Yup`. This is a validation library that will help as create validation schema for our form. We will talk about this in the next section, "Validation schema".

In this tutorial, we will use `react` and `react-dom` version `17.0.2`. The `react-scrips` will be version `4.0.3`. Formik will be version `2.2.9`. Version of Yup will be `0.32.9`. When you install these dependencies you are ready to go.

## Validation schema

For this part, we will use the same validation schema as we used in the previous part. This validation schema will contain three form fields, "name", "email" and "password". All these fields will be strings and all will be required. For the "email" field, we will want to check that any value user provides is in email format.

We could build this schema, and necessary validation logic by ourselves and connect it with Formik. We don't have to. Instead, we can use available validation libraries to do this work for us. One of these libraries is [Yup]. Thanks to this library, we can create validation schema objects Formik can use to validate all fields on our forms.

A nice thing on Yup is that it provides various methods we can use to create validation schema that fits our needs. For example, we can use method `string()` to specify that some field value must be a string. We can then make it required by using `required()` method. To make sure something is in email format?

Yup provides method `email()` that checks if the value passed into the input is in email format or not. There are many other methods, and customizations, ready to use. For this tutorial, we will stick with these three, `string()`, `required()` and `email()`. Yup also allows to define error messages for each field.

In a fact, we can define error message for each of Yup's validation methods. What this means is that we can display different message for the same field. What message will be visible will depend on current error. Creating these messages is easy. All we need is to pass these messages as strings to specific Yup method.

For example, we can define that field is required and specify a simple message for this condition: `required('Field "X" is required')`. We will use this feature in our schema as well and define different message for different validations. Nonetheless, the validation will remain simple.

```JavaScript
// Import Yup:
import * as Yup from 'yup'

// Create validation schema for form
// with three fields: "name", "email" and "password":
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'),
})
```

## Using Formik and its components

The schema is ready. Now, let's build React form using mainly Formik components. The upside of this approach is that we will not have to specify as many input element attributes as we had to in the [previous part]. This is because all form components are provided by Formik and as such are automatically connected to Formik parent instance (context).

Formik components we will need will be three, `Formik`, `Form` and `Field`. The `Form` and `Field` will be replacements for HTML `form` and `input` elements. The `Formik` component will create a parent instance and context of our Formik form. This will be the wrapper of the whole form, this includes the `Form` component.

### Formik component

The `Formik` component has multiple attributes we can use to setup and customize Formik functionality. For the purpose of this tutorial we will need three: `initialValues`, `onSubmit` and `validationSchema`. The `initialValues` attribute is on object that allows as to define all form fields and their initial values.

For our form, we will specify properties of `initialValues` (form fields) to be "name", "email" and "password". All initial values will be empty strings. As a value for the `validationSchema` attribute we will use the validation schema we created with Yup. The value of `onSubmit` will be a function Formik will use when form is submitted.

For this tutorial, we will create arrow function with `console.log()` to log values provided to the form. In your case, this is the place where you can add any logic you want to execute when someone submits the form. There is one more thing we will need from Formik. We will need access to `errors` and `touched` objects.

These two are Formik's states that keep tract of fields that contain any errors and fields that have been touched, or focused. We can expose this data from Formik very easily. This is because `Formik` component uses render-prop pattern, and allows its children to be a function that returns some React component.

This rendered component will be our form. What we can do is to tell `Formik` component to expose some data by passing them as arguments to the function it renders. This will allow use to use this exposed data anywhere in the form. Data we will pass are the `errors` and `touched` objects. We will pass them using [object destructuring].

```jsx
// Import dependencies:
import { memo } from 'react'
import { Formik, Form, Field } from 'formik'

// Create the form component:
export const FormFormik = memo(() => {
  return (
    <Formik
      initialValues={{ name: '', email: '', password: '' }}
      onSubmit={(values) => {
        console.log(values)
      }}
      validationSchema={formSchema}
    >
      {({ errors, touched }) => (
        <Form>{/* The rest of the form content */}</Form>
      )}
    </Formik>
  )
})

FormFormik.displayName = 'FormFormik'
```

### Field components and error messages

Each form field will be composed of three parts: label, field and error message. We will create the label and error message with `label` and `p` HTML element. This is because Formik doesn't render labels nor provide a dedicated component for it. It renders only input placeholders if you tell it to do so.

So, if you want to use input placeholders instead of labels you can ignore the `label` elements. Instead of `label`, you can add `placeholder` attribute for each field with appropriate text. Another two attributes we will need will be `type` and `name`. The `type` is the same as input `type` attribute that specifies the type of input.

The `name` is also the same as input `name` attribute. Aside to that, it also allows Formik to connect field with correct value in form states. This includes `initialValues`, `errors` and `touched`. This means that value of `name` for each field has to match corresponding property in `initialValues`, `errors` and `touched` and also in validation schema.

So, if our schema contains rules for fields "name", "email" and "password", values for `name` attributes has to be one of these, `name`, `email` and `password`. That's all we need, or Formik needs, for the `Field`. No need for additional attributes or handlers. Last piece are error messages.

We will render error messages as plain text wrapped in `p` elements. The important thing here is the rendering condition for each message. We want to display errors only when there are any and when user really interacted with the form. We want to avoid showing errors in an empty form that was just loaded.

To ensure this, we will use the `errors` and `touched` objects. For each field, we will first check if there are any errors for that field. We will also check if a field has been touched. Only when field has an error and was touched we will show an error. We will get these information by using the value of `name` attribute.

Last thing. We will need a button to submit the form. This can be a regular HTML `button` element with `type` set to `submit`. When clicked, this will trigger Formik's `onSubmit` method. This is the method you pass as the value to `onSubmit` attribute of `Formik` component.

```jsx
// ... previous code
<Form>
  <div>
    <label htmlFor="name">Name</label>

    {/* Create field component - renders input element */}
    <Field type="text" name="name" />

    {/* Show error if field contains error and was touched */}
    {errors.name && touched.name && <p>{errors.name}</p>}
  </div>

  <div>
    <label htmlFor="email">Email</label>

    {/* Create field component - renders input element */}
    <Field type="email" name="email" />

    {/* Show error if field contains error and was touched */}
    {errors.email && touched.email && <p>{errors.email}</p>}
  </div>

  <div>
    <label htmlFor="password">Password</label>

    {/* Create field component - renders input element */}
    <Field type="password" name="password" />

    {/* Show error if field contains error and was touched */}
    {errors.password && touched.password && <p>{errors.password}</p>}
  </div>

  <div>
    <button type="submit">Submit</button>
  </div>
</Form>
// ... rest of the code
```

### Putting it together

The `Formik` component is ready and `Field` components for each field, with error message, are ready as well. The thing that remains is to take the code we've created so far and put it together. This will give us working React form powered by Formik components, and validated by Yup.

```jsx
// Import dependencies:
import { memo } from 'react'
import { Formik, Form, Field } 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 FormFormik = memo(() => {
  return (
    <Formik
      initialValues={{ name: '', email: '', password: '' }}
      onSubmit={(values) => {
        console.log(values)
      }}
      validationSchema={formSchema}
    >
      {({ errors, touched }) => (
        <Form>
          <div>
            <label htmlFor="name">Name</label>

            {/* Create field component - renders input element */}
            <Field type="text" name="name" />

            {/* Show error if field contains error and was touched */}
            {errors.name && touched.name && <p>{errors.name}</p>}
          </div>

          <div>
            <label htmlFor="email">Email</label>

            {/* Create field component - renders input element */}
            <Field type="email" name="email" />

            {/* Show error if field contains error and was touched */}
            {errors.email && touched.email && <p>{errors.email}</p>}
          </div>

          <div>
            <label htmlFor="password">Password</label>

            {/* Create field component - renders input element */}
            <Field type="password" name="password" />

            {/* Show error if field contains error and was touched */}
            {errors.password && touched.password && <p>{errors.password}</p>}
          </div>

          <div>
            <button type="submit">Submit</button>
          </div>

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

FormFormik.displayName = 'FormFormik'
```

## Conclusion: 3 ways to build React forms with Formik pt.2

This was the alternative of the first approach of using Formik to build React forms. This approach, using mainly Formik components, can reduce HTML markup you would otherwise need. With some validation library such as Yup, you can also remove a lot of code you would otherwise need for validation logic. I hope that this tutorial helped you learn how to do both.

[create-react-app]: https://create-react-app.dev
[first part]: https://blog.alexdevero.com/build-react-forms-formik-pt-1/
[formik]: https://formik.org/
[youp]: https://www.npmjs.com/package/yup
[previous part]: https://blog.alexdevero.com/build-react-forms-formik-pt-1/#the-form-content
[arrow function]: https://blog.alexdevero.com/javascript-arrow-functions/
[object destructuring]: https://blog.alexdevero.com/destructuring-assignment-javascript/#destructuring-objects