If you're building a React application that collects data from your users, forms provide a familiar and simple interface to get the job done.

You can build all kinds of forms in React, but depending on how complex you want to go, you'll need you'll need to consider whether you want to build from scratch or use an existing React form library.

If you need a really basic form with limited functionality, pure React and HTML might be all you need. However, once your needs require complicated interfaces or custom logic it may be time to turn to a React form library.

React form libraries help tap into a lot of feature functionality without needing to build everything from scratch. Many developers turn to React form libraries to manage complex form handling like data validation, styling, and dynamic rendering so they can focus more closely on business logic.

In this post, we’ve compiled three popular React form libraries—Formik, react-hook-form, and react-final-form—and actionable guidance to choose the right React form library for your next project.

Formik

Formik is a form library that allows you to create forms in a declarative manner that’s easy to read and understand. It's by far the most popular React form library on the list, based on GitHub popularity.

It also has a big community behind it, so if you have a question, want to look at examples of other people’s code, or fork a project as a jumping-off point, you’re all set.

To get started, add it to your project like so:

yarn add formik

You can use it in your React component like so:

import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';

const LoginForm = () => (
 <div>
   <Formik
     initialValues={{ email: '', password: '' }}
     validate={values => {
       const errors = {};
       if (!values.email) {
         errors.email = 'Required';
       } else if (
         !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
       ) {
         errors.email = 'Invalid email address';
       }
       return errors;
     }}
     onSubmit={(values, { setSubmitting }) => {
       setTimeout(() => {
         alert(JSON.stringify(values, null, 2));
         setSubmitting(false);
       }, 400);
     }}
   >
     {({ isSubmitting }) => (
       <Form>
         <Field type="email" name="email" />
         <ErrorMessage name="email" component="div" />
         <Field type="password" name="password" />
         <ErrorMessage name="password" component="div" />
         <button type="submit" disabled={isSubmitting}>
           Submit
         </button>
       </Form>
     )}
   </Formik>
 </div>
);
export default LoginForm;

Formik comes with a few helper components that make it easier to work with forms: <Form />, <Field />, and <ErrorMessage />. Formik keeps track of your form’s state and exposes a few reusable methods and event handlers (handleChange, handleBlur, and handleSubmit). Formik uses the name or id attribute to figure out which fields to update.

It also provides an interface that allows you to write custom validation logic for your form and allows you to write your logic for form submission, which is automatically bound to the form. The declarative nature of Formik makes the code easily readable, which many developers tend to appreciate.

In terms of size, it adds 13.1kb to your JavaScript bundle, which makes it the largest form library on the list. This could potentially slow things down if forms need to load multiple times across the site, but in general it's still an acceptable weight if your site is quite large. However, if your site is small and very lightweight, you might want to consider another solution.

Overall, if you're looking for a declarative React form library that provides a great user experience and exposes an interface to write your own validation and business logic, then Formik will be a good fit for you.

React-hook-form

As the name implies, React-hook-form was built with the introduction of React hooks in React version 16.8 (2018), so it’s relatively newer and was built to leverage the power of React hooks. As such, in terms of performance, it beats the other alternatives in this article.

This is because the approach it uses to manage form state isolates component re-renders to the bare minimum. This means not every state change causes a re-render of the component and child components. And that, in turn, results in snappy interactions for the end user.

However, unless you're building a truly complex form with multiple interactions, the performance gains won't really be perceivable to the end user. There are also known compatibility issues with popular UI libraries, like MaterialUI, that may pose a challenge if that’s your UI library of choice.

You can get started with it by running:

yarn add react-hook-form

Once set, you can use it like so:

import React from "react";
import { useForm } from "react-hook-form";

export default function LoginForm() {
 const { register, handleSubmit, errors } = useForm();
 const onSubmit = (data) => {
   console.log(data);
   };

 return (
   <form onSubmit={handleSubmit(onSubmit)}>
     <input name="email" ref={register({ required: true })} />
     {errors.email && <span>This field is required</span>}
     <input name="password" ref={register({ required: true })} />
     {errors.password && <span>This field is required</span>}
     <button type="submit">Submit</button>
   </form>
 );
}

As you can see in the above snippet, the code is quite readable and straightforward (if you understand hooks, that is). It uses ref to handle state changes, so it causes minimal re-renders. It exposes the useForm hook, which has a couple of helper methods to register state, handle form submissions, and keep track of errors.

Will your React application be dealing with complex forms that have various user interactions? Are you concerned about keeping them performing well? If so, then React-hook-form will be a good fit for your purposes.

React-final-form

React-final-form is an attempt to resolve some of the shortcomings of Redux-form developed by the same author. The goals of the library include minimal bundle size (about 3.2kb minified and gzipped), high performance, strong typing (for Flow or TypeScript), and modularity.

React-final-form just concerns itself with getting form values with synthetic events and managing subscriptions to form fields. Let’s look at a simple login form to see it in action.

You can add it to your React project by running:

yarn add react-final-form

Then go ahead and use it like so:

import { Form, Field } from 'react-final-form'
import Styles from './Styles'

const onSubmit = async values => {
 window.alert(JSON.stringify(values, 0, 2))
}

const LoginForm = () => (
 <Styles>
   <Form
     onSubmit={onSubmit}
     render={({ handleSubmit, form, submitting, pristine, values }) => (
       <form onSubmit={handleSubmit}>
         <div>
           <label>Email</label>
           <Field
             name="email"
             component="input"
             type="email"
             placeholder="Enter Email"
           />
         </div>
         <div>
           <label>Password</label>
           <Field
             name="password"
             component="input"
             type="password"
             placeholder="Enter Password"
           />
         </div>
         <div className="buttons">
           <button type="submit" disabled={submitting || pristine}>
           Submit
           </button>
           <button
             type="button"
             onClick={form.reset}
             disabled={submitting || pristine}
           >
             Reset
           </button>
         </div>
         <pre>{JSON.stringify(values, 0, 2)}</pre>
       </form>
     )}
   />
 </Styles>
)
export default LoginForm;

React-final-form provides the helper components <Form /> and <Field /> that you can use to build your form. <Field /> has a component prop that you can use to specify the type of input: <input />, <select />, etc. The type prop also specifies the type of input — this could be text, checkbox, radio, or any other valid HTML input types.

<Form /> has an onSubmit prop that you can use to handle the form submission. It also exposes a render prop that exports a couple of useful parameters. For example, pristine is a Boolean flag that shows if the form has been interacted with. This is useful if you want to disable the submit button until the user has filled the required form fields, for example. Submitting lets you know if the form submission is in progress, so the submit button is also disabled.

The last thing to note is the root <Style> component. This is a styled component that you can use to style the form.

The imported ./Styles file looks like so:

import styled, { css } from 'styled-components'

const btn = (light, dark) => css`
 white-space: nowrap;
 display: inline-block;
 border-radius: 5px;
 padding: 5px 15px;
 font-size: 16px;
 color: white;
 &:visited {
   color: white;
 }
 background-image: linear-gradient(${light}, ${dark});
 border: 1px solid ${dark};
 &:hover {
   background-image: linear-gradient(${light}, ${dark});
   &[disabled] {
     background-image: linear-gradient(${light}, ${dark});
   }
 }
 &:visited {
   color: black;
 }
 &[disabled] {
   opacity: 0.6;
   cursor: not-allowed;
 }
`

...

Real-world usage of React form libraries

The chart below shows usage data from npm trends over six months:

image2-4

Usage data for Formik, React-final-form, and React-hook-form according to npm trends

As you can see, Formik is by far the most popular in terms of usage among the three React form libraries. React-hook-form is the next-most-used form library, even though it’s relatively new.

You can see the results in the table below:

image1-4

Statistics of React form libraries

React-hook-form appears to be the fastest growing in terms of GitHub stars and usage. The table also shows that all the libraries are actively being developed, as shown in the updated column. Overall, if you want a more mature and robust library, opt for formik or react-final-form. But if you want to be on the forefront of technology, go for react-hook-form: the newest libraries of the group.

Which React form library should you choose?

Formik has a large community behind it because it’s the longest-running form library of the lot. Its declarative nature makes the code easily readable, which is great for developer experience.

Formik makes it straightforward to write your own custom validation logic without much hassle, and it integrates seamlessly with the popular validation library Yup. If these are factors you consider, then Formik will be a good fit for you.

React-final-form is the smallest form library in terms of size (3.2kb minified and gzipped). That means it doesn't increase your JavaScript bundle by much. It also gives you control over the appearance of your form using styled components. If you want control over the styling of your form without upsetting your final bundle, it's a good option.

React-hook-form is the fastest-growing form library among the three libraries. Its adoption of React hooks makes it a wise choice if you're working with React hooks. In terms of performance, it’s the winner, as it avoids unnecessary re-renders. So if you’ll be building complex forms with a lot of interactions, use React-hook-form.

By now, you should have a clear idea of where each React form library holds strengths and weaknesses compared to the others. Depending on your project, you can now choose the best library to help you build forms in your React code. What will you use next?