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 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:
1yarn add formik
2
You can use it in your React component like so:
1import React from 'react';
2import { Formik, Form, Field, ErrorMessage } from 'formik';
3
4const LoginForm = () => (
5 <div>
6 <Formik
7 initialValues={{ email: '', password: '' }}
8 validate={values => {
9 const errors = {};
10 if (!values.email) {
11 errors.email = 'Required';
12 } else if (
13 !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
14 ) {
15 errors.email = 'Invalid email address';
16 }
17 return errors;
18 }}
19 onSubmit={(values, { setSubmitting }) => {
20 setTimeout(() => {
21 alert(JSON.stringify(values, null, 2));
22 setSubmitting(false);
23 }, 400);
24 }}
25 >
26 {({ isSubmitting }) => (
27 <Form>
28 <Field type="email" name="email" />
29 <ErrorMessage name="email" component="div" />
30 <Field type="password" name="password" />
31 <ErrorMessage name="password" component="div" />
32 <button type="submit" disabled={isSubmitting}>
33 Submit
34 </button>
35 </Form>
36 )}
37 </Formik>
38 </div>
39);
40export default LoginForm;
41
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.
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:
1yarn add react-hook-form
2
Once set, you can use it like so:
1import React from "react";
2import { useForm } from "react-hook-form";
3
4export default function LoginForm() {
5 const { register, handleSubmit, errors } = useForm();
6 const onSubmit = (data) => {
7 console.log(data);
8 };
9
10 return (
11 <form onSubmit={handleSubmit(onSubmit)}>
12 <input name="email" ref={register({ required: true })} />
13 {errors.email && <span>This field is required</span>}
14 <input name="password" ref={register({ required: true })} />
15 {errors.password && <span>This field is required</span>}
16 <button type="submit">Submit</button>
17 </form>
18 );
19}
20
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 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:
1yarn add react-final-form
2
Then go ahead and use it like so:
1import { Form, Field } from 'react-final-form'
2import Styles from './Styles'
3
4const onSubmit = async values => {
5 window.alert(JSON.stringify(values, 0, 2))
6}
7
8const LoginForm = () => (
9 <Styles>
10 <Form
11 onSubmit={onSubmit}
12 render={({ handleSubmit, form, submitting, pristine, values }) => (
13 <form onSubmit={handleSubmit}>
14 <div>
15 <label>Email</label>
16 <Field
17 name="email"
18 component="input"
19 type="email"
20 placeholder="Enter Email"
21 />
22 </div>
23 <div>
24 <label>Password</label>
25 <Field
26 name="password"
27 component="input"
28 type="password"
29 placeholder="Enter Password"
30 />
31 </div>
32 <div className="buttons">
33 <button type="submit" disabled={submitting || pristine}>
34 Submit
35 </button>
36 <button
37 type="button"
38 onClick={form.reset}
39 disabled={submitting || pristine}
40 >
41 Reset
42 </button>
43 </div>
44 <pre>{JSON.stringify(values, 0, 2)}</pre>
45 </form>
46 )}
47 />
48 </Styles>
49)
50export default LoginForm;
51
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:
1import styled, { css } from 'styled-components'
2
3const btn = (light, dark) => css`
4 white-space: nowrap;
5 display: inline-block;
6 border-radius: 5px;
7 padding: 5px 15px;
8 font-size: 16px;
9 color: white;
10 &:visited {
11 color: white;
12 }
13 background-image: linear-gradient(${light}, ${dark});
14 border: 1px solid ${dark};
15 &:hover {
16 background-image: linear-gradient(${light}, ${dark});
17 &[disabled] {
18 background-image: linear-gradient(${light}, ${dark});
19 }
20 }
21 &:visited {
22 color: black;
23 }
24 &[disabled] {
25 opacity: 0.6;
26 cursor: not-allowed;
27 }
28`
29
30...
31
The chart below shows usage data from npm trends over six months:
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:
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.
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?
Reader