Data Validation with React Final Form
Tags: react, validation
Published: 2023-03-15 01:05:46

by [Clarence Bakosi](https://blog.openreplay.com/authors/clarence-bakosi)

Form validation is critical for preventing malicious attacks or incorrect data from being inputted into an application. The [React Final Form](https://final-form.org/react) zero-dependency library is a form validation tool that provides form management for validating forms in react applications. It is written in core JavaScript, which means it can be used in other JavaScript frameworks. In this article, we will look at this library and how to use it in a web application. 

## Getting started with React Final Form

In this section, we will create a React Application by running the following command in your project terminal:

```bash
npx create-react-app react-final-form
cd react-final-form
```

We will install React Final Form by using either of the code blocks below:

```bash
yarn add final-form react-final-form final-form-focus
```

or

```bash
npm install final-form react-final-form final-form-focus
```

To style our application, replace the code in `App.css` by pasting the entire code block from this [link](https://github.com/clarencepenz/react-final-form/blob/main/src/App.css)

## Validating Forms with React Final Form

It is very simple to validate forms using the React Final Form library; we will look at record-level validation, field-level validation, and error-focused validation.

To make use of the library, use the code block below:

```javascript
import './App.css';
import React from 'react'
import { Form, Field } from 'react-final-form'

const App = () =>  {
  const logValues = values => {
    window.alert(JSON.stringify(values, 0, 2))
  }
  
  return (
    <div className='App'>
    <main >
    <h1>React Final Form</h1>
  
    <Form
      onSubmit={logValues}
      initialValues={{ firstName: "John"}}
    >
      {({ handleSubmit }) => (
        <form className="form" onSubmit={handleSubmit}>
          <div>
            <label>First Name</label>
            <Field
              name="firstName"
              component="input"
              type="text"
              placeholder="First Name"
              className="Input"
            />
          </div>
          <div className="button-container">
            <button type="submit">
              Submit
            </button>
          </div>
        </form>
      )}
      
      </Form>
    </main>
  </div>
  )
}

export default App;
```

We imported `Form` and `Field` from `react-final-form` into the above code block. The only required prop for the `Form` component is the `onSubmit` prop. The `initialValues` and `render` props are optional; the `render` prop can be passed to the `Form` component as a child. It provides the `handleSubmit` function via the `render` prop, passed to the HTML `form` onSubmit. We will then create a `Field` component with the required/optional props.

![Basic use of React Final Form](https://blog.openreplay.com/images/data-validation-with-react-final-form/images/pP5iOL1.png)

## Record level validation

Record-level validation utilizes a `validate` prop on the `Form` component, which contains all form values and returns an object containing the structure of the values in the `Form` component.

```javascript
...
  return (
    <div className="App">
      <main>
        <h1>React Final Form</h1>

        <Form
          onSubmit={logValues}
          validate={(values) => {
            const errors = {};
            if (!values.username) {
              errors.username = "required";
            }
            if (!values.password) {
              errors.password = "Required";
            }
            if (!values.confirm) {
              errors.confirm = "Required";
            } else if (values.confirm !== values.password) {
              errors.confirm = "Must match";
            }
            return errors;
          }}
        >
          {({ handleSubmit, submitting, form, pristine }) => (
            <form onSubmit={handleSubmit}>
              <Field name="username">
                {({ input, meta }) => (
                  <div>
                    <label>Username</label>
                    <input
                      {...input}
                      type="text"
                      className="Input"
                      placeholder="Username"
                    />
                    <span className="errorMsg">
                      {meta.error && meta.touched && <span>{meta.error}</span>}
                    </span>
                  </div>
                )}
              </Field>
              <Field name="password" type="password">
                {({ input, meta }) => (
                  <div>
                    <label>Password</label>
                    <input
                      {...input}
                      className="Input"
                      type="password"
                      placeholder="Password"
                    />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <Field name="confirm" type="password">
                {({ input, meta }) => (
                  <div>
                    <label>Confirm</label>
                    <input {...input} className="Input" placeholder="Confirm" />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <div className="button-container">
                <button type="submit" disabled={submitting}>
                  Submit
                </button>
                <button
                  type="button"
                  onClick={form.reset}
                  disabled={submitting || pristine}
                  className="reset"
                >
                  Reset
                </button>
              </div>
            </form>
          )}
        </Form>
      </main>
    </div>
  );
...
```

Using the `validate` prop on the `Form` component, we can check if the values passed into the `Form` are valid or invalid in the above code block. Other useful values in the render prop we destructured include `submitting`, `form`, and `pristine`, which we can use to validate the form when we click the submit or reset button.

The `Field` components have a `fieldState` that contains the `input` and `meta` properties. These are destructured from `fieldState`, and we added the HTML `input` element as a child to the `Field` component, spreading the `input` from the `fieldState` to the HTML `input`. `meta` is a helpful property from `feildState`, it has a few values that are used to keep track of the state of the field, this can be used to display helpful error messages on the form using `meta.touched` and `meta.error`.

![Record level Validation](https://blog.openreplay.com/images/data-validation-with-react-final-form/images/45m6OEw.gif)


<h2>Session Replay for Developers</h2> 
<p><em>
Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data
</em></p>
<img alt="OpenReplay" width="768" height="400" src="https://blog.openreplay.com/assets/overview_Z17Qdyg.png" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em>





## Field-level validation

The `Field` component has a `validate` prop to which we can directly pass validation rules; if the value is valid, it returns undefined; otherwise, it returns the error message.

Using the Field-level validate prop, we can successfully define custom validation rules.

```javascript
...
const App = () => {
...

  const required = (value) => (value ? undefined : "Required");
    
  const mustBeNumber = (value) =>
    isNaN(value) ? "Must be a number" : undefined;
    
  const minValue = (min) => (value) =>
    isNaN(value) || value >= min ? undefined : `Should be greater than ${min}`;
    
  const composeValidators =
    (...validators) =>
    (value) =>
      validators.reduce(
        (error, validator) => error || validator(value),
        undefined
      );
...

  return (
    <div className="App">
      <main>
        <h1>React Final Form</h1>

        <Form
          onSubmit={logValues}
          validate={(values) => {
            const errors = {};
            if (!values.username) {
              errors.username = "required";
            }
            return errors;
          }}
        >
          {({ handleSubmit, submitting, form, pristine }) => (
            <form onSubmit={handleSubmit}>
              <Field name="firstName" validate={required}>
                {({ input, meta }) => (
                  <div>
                    <label>First Name</label>
                    <input {...input} className="Input"  type="text" placeholder="First Name" />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <Field
                name="age"
                validate={composeValidators(
                  required,
                  mustBeNumber,
                  minValue(18)
                )}
              >
                {({ input, meta }) => (
                  <div>
                    <label>Age</label>
                    <input {...input} className="Input"  type="text" placeholder="Age" />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <div className="button-container">
                <button type="submit" disabled={submitting}>
                  Submit
                </button>
                <button
                  type="button"
                  onClick={form.reset}
                  disabled={submitting || pristine}
                  className="reset"
                >
                  Reset
                </button>
              </div>
            </form>
          )}
        </Form>
      </main>
    </div>
  );
};
...
```

Four sets of field-level validation rules were created in the preceding code block: `required`, `mustBeNumber`, `minValue`, and `composeValidators`. To validate a component, the `composeValidators` can accept multiple validation rules. We can combine record and field-level validations to add validation to a form.

![Field level valid](https://blog.openreplay.com/images/data-validation-with-react-final-form/images/aFHuZDx.gif)

## Error-focused validation

We can validate other `input` fields, `select`, `checkbox`, and `radio` buttons using record-level validation. You can also add the `decorators` prop to the `Form` component if you have a large form and want to easily help a user auto-detect where validation failed when submitting a form.

```javascript
...
import createDecorator from "final-form-focus";
...
const App = () => {
...
  const focusOnError = createDecorator();
...
  return (
    <div className="App">
      <main>
        <h1>React Final Form</h1>
        <Form
          decorators={[focusOnError]}
          onSubmit={logValues}
          validate={(values) => {
            const errors = {};
            if (values.username === undefined) {
              errors.username = "Required";
            }
            if (values.position === undefined) {
              errors.position = "Required";
            }
            if (!values.terms) {
              errors.terms = "Required";
            }
            if (!values.interested) {
              errors.interested = "Required";
            }

            return errors;
          }}
        >
          {({ handleSubmit, submitting, form, pristine }) => (
            <form className="form" onSubmit={handleSubmit}>
              <Field name="username">
                {({ input, meta }) => (
                  <div className={meta.active && "active" }>
                    <label>Username</label>
                    <input
                      {...input}
                      className="Input"
                      type="text"
                      placeholder="Username"
                    />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <Field name="position" type="select">
                {({ input, meta }) => (
                  <div className={meta.active && "active" }>
                    <label>Position</label>
                    <select {...input}>
                      <option value="">-- Position --</option>
                      <option value="writer">Writer</option>
                      <option value="engineer">Engineer</option>
                      <option value="analyst">Analyst</option>
                      <option value="hr">HR</option>
                    </select>
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <Field name="terms" type="checkbox">
                {({ input, meta }) => (
                  <div className={meta.active && "active" }>
                    <label>Agree with terms</label> <input {...input} />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <h3>Want more offers?</h3>
              <Field name="interested" type="radio" value="interested">
                {({ input, meta }) => (
                  <div className={meta.active && "active" }>
                    <label>Yes</label>
                    <input {...input} />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <Field name="interested" type="radio" value="not-interested">
                {({ input, meta }) => (
                  <div className={meta.active && "active" }>
                    <label>No</label>
                    <input {...input} />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <div className="button-container">
                <button type="submit" disabled={submitting}>
                  Submit
                </button>
                <button
                  type="button"
                  onClick={form.reset}
                  disabled={submitting || pristine}
                  className="reset"
                >
                  Reset
                </button>
              </div>
            </form>
          )}
        </Form>
      </main>
    </div>
  );
};
...
```

To know when a field is active, `meta.active` is set to true. We also used the `createDecorator` function from `final-form-focus` and assigned it to `focusOnError`, which we passed to the `decorators` prop on the `Form` component. This helps to auto-detect when a field is invalid upon submitting the form.

![Complete form validation](https://blog.openreplay.com/images/data-validation-with-react-final-form/images/6zmOQXJ.gif)

## Conclusion

It is incredibly simple and straightforward to manage a form's state and validate it using React Final Form. Using the library in a React application, you may create straightforward intricate forms.

For additional details on the library, check the official documentation. To examine the complete code, check out this [GitHub repository](https://github.com/clarencepenz/react-final-form).




[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)