Diogo Souza Brazilian dev. Creator of altaluna.com.br

Comparing React form builders: Formik vs. Unform

6 min read 1687

Editor’s note: This post was updated 1 November 2021.

The more modern the application, the more likely developers will need to use special features and helpful hints to ensure demanding clients are happy with their user experience.

In the world of React, forms give us all the power of input components — but this power isn’t enough.

We need better and faster ways to create customized components involving inputs, selects, buttons, and potentially new components that are not recognizable by our browsers implicitly (i.e., create new UI experiences in terms of components).

We need to validate data in many different forms, sometimes via complex regex or JavaScript validation functions, and sometimes through external resources. In certain cases, one might need to store data locally in the navigator and recover it wisely. Others might need to communicate with those components in their own way.

React doesn’t give developers a way to deal with custom highlights, so the community came up with ways to do it themselves.

There are dozens of different options, such as libs for basic form manipulation, integration with Redux, and the like. At this point, the best option for users seems to be Formik — at least that’s what the numbers show us.

What is Formik?

The image below displays the most downloaded npm packages for famous React form libraries (as per the writing of this article) at npmtrends.com:

Formik is by far the favorite. Not only is it flexible and communicative with React, but it also allows developers to easily integrate with Yup (a popular JavaScript object schema validator and object parser).

We made a custom demo for .
No really. Click here to check it out.

Perhaps its most important feature is form state management — we no longer need to keep calling the Redux store’s state on every keystroke (which is indeed bad practice) because state is automatically maintained by Formik locally.

What is Unform?

While Formik is good with controlled components, it’s not as adept at handling uncontrolled ones.

Unform, on the other hand, is focused on offering high performance for React forms and nested structures (particularly deep ones). Unform also allows you to create strong relationships between your components — even the uncontrolled components — without sacrificing anything performance-wise. This Brazilian React library also works very well with React Hooks.

In this article, we’re going to look at a few examples that demonstrate the potential of this library.

Form creation using Formik and Unform

First, let’s take a look at how both libraries handle form creation. Below, we can see a basic Formik form usage:

import React from 'react';
import { Formik } from 'formik';

const SampleForm = () => (
  <div>
    <Formik
      initialValues={{ email: '', password: '' }}
      validate={values => {
        const errors = {};
        
        // error validations here

        return errors;
      }}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          console.log(values);

          // submit logic here
          
          setSubmitting(false);
        }, 400);
      }}
    >
      {({
        values,
        errors,
        touched,
        handleChange,
        handleBlur,
        handleSubmit,
        isSubmitting,
      }) => (
        <form onSubmit={handleSubmit}>
          // fields
          {errors.email && touched.email && errors.email}
          
          <button type="submit" disabled={isSubmitting}>
            Submit
          </button>
        </form>
      )}
    </Formik>
  </div>
);

export default SampleForm;

Refer to the official React docs for React specifications. Most of these libraries usually advise that developers start with the JavaScript submit function.

In our case, this function has two parameters: values, which represents the form fields’ values, and a second object with properties and functions from Formik for free use in the submit body function.

The setSubmitting (a boolean), for example, is a useful mechanism for analyzing whether the request is currently happening or not.

Each Formik form is made of the main element <Formik> and some important props:

  • initialValues: the local state value for each subsequent controlled component
  • validate: receives all the form’s values as parameters. You can use this function to perform whatever validations you want. You can also use it to set and return the proper error codes/messages
  • onSubmit: determines which function will handle the submit event
  • render: the implicit form render function itself. Decide which are the controlled Formik components and which are the uncontrolled HTML components of your form

Pretty simple, isn’t it? Let’s take a look at the equivalent Unform form below. Make sure to have react, react-dom, and yup packages already installed.

import React from 'react'
import { Form } from '@unform/web'
import { useField } from '@unform/core'
import * as Yup from 'yup';

function Input({ name, ...rest }) {
    const inputRef = React.useRef(null)
    const { fieldName, defaultValue, registerField, error } = useField(name)
  
    React.useEffect(() => {
      registerField({
        name: fieldName,
        ref: inputRef,
        getValue: ref => {
          return ref.current.value
        },
        setValue: (ref, value) => {
          ref.current.value = value
        },
        clearValue: ref => {
          ref.current.value = ''
        },
      })
    }, [fieldName, registerField])
  
    return <input ref={inputRef} defaultValue={defaultValue} {...rest} />
  }
  
  function SampleForm() {
    const formRef = React.useRef(null);

    const initialValues = {
        email: '',
        document: ''
    };
    
    async function handleSubmit(data) {
        try {
            // Remove all previous errors
            formRef.current.setErrors({});
            const schema = Yup.object().shape({
              email: Yup.string()
                .email()
                .required(),
              password: Yup.string()
                .min(6)
                .required(),
            });
            await schema.validate(data, {
              abortEarly: false,
            });

            // Validation passed
            console.log(data);
        } catch (err) {
            const validationErrors = {};
            if (err instanceof Yup.ValidationError) {
              err.inner.forEach(error => {
                validationErrors[error.path] = error.message;
              });
              formRef.current.setErrors(validationErrors);
            }
        }
    }
  
    return (
      <Form ref={formRef} initialData={initialValues} onSubmit={handleSubmit}>
        <Input name="email" type="email" />
        <Input name="password" type="password" />
  
        <button type="submit">Sign in</button>
      </Form>
    )
  }
  
  render(
    <>
      <SampleForm />
    </>
  )

First off, we need to install the Unform via:

yarn add @unform/web @unform/core
# or
npm i @unform/web @unform/core

Now we need to import the respective Form and Input components from Unform. The second thing you’ll see is related to a Yup validation schema.

Like Formik, Unform easily integrates with Yup schemas by providing a schema property you can later use to validate the form input values. Because Yup is, by far, the most popular lib for input values validation, it’s pretty straightforward to use.

This code provides a single example to help you better understand Unform with validations like email, required fields, and minimum value length.

When Unform works with Hooks, the class-based component style is abandoned in favor of a single-function component.

The initialValues from Formik translates to initialData here — make sure to match each object property to each input name to ensure values are applied correctly. The handleSubmit function loses the parameters from Formik and simply receives the values for manipulation in the submit event.

Lastly, there’s no internal render function, which means that <Form> must be mixed with other components.

You can also use other common properties like placeholder, style, etc.

Additional elements in Formik and Unform

Select dropdowns

Let’s analyze a second example with combo boxes, which are pretty common elements we need in forms.

Select dropdowns usually look like this in Formik:

<Field as="select" name="color">
    <option value="red">Red</option>
    <option value="green">Green</option>
    <option value="blue">Blue</option>
</Field>

Not complex. Unform, on the other hand, partners with react-select to make this happen. Below’s an example of how to combine an external dropdown component with Unform:

import React, { useRef, useEffect } from 'react';
import ReactSelect, {
  OptionTypeBase,
  Props as SelectProps,
} from 'react-select';
import { useField } from '@unform/core';

interface Props extends SelectProps<OptionTypeBase> {
  name: string;
}

export default function Select({ name, ...rest }: Props) {
  const selectRef = useRef(null);
  const { fieldName, defaultValue, registerField, error } = useField(name);

  useEffect(() => {
    registerField({
      name: fieldName,
      ref: selectRef.current,
      getValue: (ref: any) => {
        if (rest.isMulti) {
          if (!ref.state.value) {
            return [];
          }
          return ref.state.value.map((option: OptionTypeBase) => option.value);
        }
        if (!ref.state.value) {
          return '';
        }
        return ref.state.value.value;
      },
    });
  }, [fieldName, registerField, rest.isMulti]);

  return (
    <ReactSelect
      defaultValue={defaultValue}
      ref={selectRef}
      classNamePrefix="react-select"
      {...rest}
    />
  );
};

Pay special attention to the useRef function that connects the field with its ReactSelect counterpart.

Nested elements

When it comes to multiple and nested elements, no library provides a fully adaptable and working solution.

Formik has a very handy object called <FieldArray>, which helps with common array/list manipulations:

let countries = ['andorra', 'argentina', 'aruba'];

<Form>
   <FieldArray
      name="countries"
      render={arrayHelpers => (
          // defining your form, loop through `countries`
     )}
    />
</Form>

It also has a bunch of familiar functions like pop, replace, push, insert, and others for the automatically injected arrayHelpers that help with item manipulation.

However, whenever you want to nest items and apply validations or organize the forms in a way that’s closer to your entity model, Formik lacks options.

Unform has an interesting mechanism for dealing with nested objects. Take the following code as an example:

import React from 'react';
import { Form, Input, Scope } from '@rocketseat/unform';

function App() {
  function handleSubmit(values) {
    console.log(values);
  }

  return (
    <Form onSubmit={handleSubmit}>
      <Input name="name" />

      <Scope path="address">
        <Input name="country" />
        <Input name="zipCode" />
      </Scope>

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

Scope is an Unform component that marks the root of your nested element. It’s just for markup purposes and doesn’t hold any value.

When you submit the form your values object would look like this:

{
   name: '',
   address: { country: "", zipCode: "" }
}

Whenever you update the state’s value, it will reflect on your form fields.

Conclusion

When choosing between Formik and Unform, it’s all about finding the best fit for your project purpose.

Unform is a great library, especially because it’s lightweight, performative, and flexible enough to allow integration with other libraries. You may want to use a third-party component in your forms, like react-select and react-datepicker. With Unform, that’s easy to do.

Go ahead and try it yourself. Migrate some components, or create components from scratch. Make use of React Hooks for more concise code, and test a different fields organization for nested elements.

And don’t forget to check the official docs for more on the other elements, as well as examples of each one.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Diogo Souza Brazilian dev. Creator of altaluna.com.br

One Reply to “Comparing React form builders: Formik vs. Unform”

Leave a Reply