Handbook
Form Handling

Effective Forms in React

Last updated by Noel Varanda (opens in a new tab),
React
Form Handling
Validation
React Hook Form
Zod
Yup

Powerful form handling libraries simplify capturing user input, validating data, and managing form state, improving development efficiency.

Recommendation: For optimal form handling in React, use React Hook Form (opens in a new tab) with Zod (opens in a new tab) for state management and validation.

Requirements

When creating React forms, we have the following requirements:

  1. Manage form state efficiently by synchronizing data, handling user interactions, and validation errors.

  2. Provide a flexible validation system to define rules, handle various scenarios, and display error messages.

  3. Track and synchronize input values with state, handle changes, and manage error propagation effectively.

  4. Handle form submission, including validation, network requests, error handling, and user feedback.

These requirements highlight the complexity involved in creating forms and emphasize the need for efficient form handling solutions.

React Hook Form

React Hook Form is a lightweight and powerful library that simplifies form handling in React. It provides an intuitive API and leverages the power of React hooks to manage form state and validation. Some key advantages of React Hook Form include:

  • Simplified form state management: React Hook Form allows you to manage form state with minimal code and provides built-in validation capabilities.

  • Excellent performance: By optimizing re-renders and minimizing unnecessary updates, React Hook Form ensures optimal performance for form handling.

  • Seamless integration: React Hook Form integrates smoothly with existing React codebases, making it easy to adopt and incorporate into your projects.

For more details and usage examples, refer to the React Hook Form documentation (opens in a new tab).

Take the example above. If we want to create this using react-hook-form, it's as simple as the following snippet (more on validation later):

import { useForm } from 'react-hook-form';
 
interface FormData {
  firstName: string;
  lastName: string;
}
 
export default function CustomForm() {
  const { register, handleSubmit } = useForm<FormData>();
  const onSubmit = (data: FormData) => console.log(data);
 
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('firstName')} />
      <input {...register('lastName', { required: true })} />
      <button type="submit">Submit</button>
    </form>
  );
}

Error handling

Easily handle and display validation errors in a form:

export default function CustomForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>();
  const onSubmit = (data: FormData) => console.log(data);
 
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('firstName')} />
      {errors.firstName && <span>This field is required</span>}
 
      <input {...register('lastName', { required: true })} />
      {errors.lastName && <span>This field is required</span>}
 
      <button type="submit">Submit</button>
    </form>
  );
}

Read more on handling errors in react-hook-form (opens in a new tab).

Component libraries

Easily integrate the form handling library with external component libraries like Material-UI, simply use the <Controller /> (opens in a new tab) component:

export default function CustomForm() {
  const { register, control, handleSubmit } = useForm<FormData>();
  const onSubmit = (data: FormData) => console.log(data);
 
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="firstName"
        control={control}
        render={({ field }) => <Input {...field} />}
      />
      <input {...register('lastName', { required: true })} />
      <button type="submit">Submit</button>
    </form>
  );
}

Read more on integration with UI libraries in react-hook-form (opens in a new tab).

Global state

React Hook Form can be seamlessly integrated with both Redux and React Context to manage form state. By dispatching actions or updating the context, you can centralize the form data and make it accessible across different components. This allows you to perform additional logic or navigate to other pages after updating the store or context.


Read more on integrating with global state in react-hook-form (opens in a new tab).

Redux

To update the Redux store with the form data when the form is submitted, you can dispatch an action with the form data in the onSubmit event handler. Here's an example:

import { useDispatch } from 'react-redux';
import { submitForm } from '../redux/actions'; // Assuming you have a submitForm action
 
export default function CustomForm() {
  const { register, handleSubmit } = useForm<FormData>();
  const dispatch = useDispatch();
 
  const onSubmit = (data: FormData) => {
    dispatch(submitForm(data)); // Dispatching the submitForm action with the form data
  };
 
  // rest of component...
}

React Context

Similarly you can update react context.

import { FormContext } from '../context/FormContext'; // Assuming you have a FormContext
 
export default function CustomForm() {
  const { register, handleSubmit } = useForm<FormData>();
  const formContext = useContext(FormContext); // Accessing the FormContext
 
  const onSubmit = (data: FormData) => {
    formContext.dispatch({ type: 'SUBMIT_FORM', payload: data });
  };
 
  // rest of component...
}

Events

Additionally, react-hook-forms makes it easy to hook into the state of the form at different event stages.

onSubmit

We've seen above how to fire an event whenever the form changes.

onChange

If you want to fire an event whenever a change occurs in the form inputs, you can use the watch function from react-hook-form. Here's an example:

import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
 
export const CustomForm = () => {
  const { register, watch } = useForm<FormData>();
  const dispatch = useDispatch();
  const formData = watch(); // Retrieve the form data
 
  useEffect(() => {
    console.log(formData);
  }, [formData]);
 
  // rest of component...
};

Validation

When it comes to form validation, we recommend using Zod as the validation library of choice. Zod offers strong typing and runtime validation capabilities, with concise and expressive syntax for defining and applying validation rules. Although Yup is widely used and supports complex validation rules, Zod's TypeScript-first approach and runtime validation make it a compelling alternative.

Zod ⭐️

Zod (opens in a new tab) is a widely used validation library that offers strong typing and runtime validation capabilities. Some key features of Zod include:

  • TypeScript integration: Zod is built with TypeScript in mind and provides excellent type inference and checking.

  • Concise API: Zod's API is designed to be concise and expressive, making it easier to define and apply validation rules.

  • Runtime validation: Zod performs runtime validation, allowing you to validate data even outside the context of form submission.

The following code adds zod to the react-hook-form above:

import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
 
const schema = z.object({
  firstName: z.string().nonempty({ message: 'First name is required' }),
  lastName: z.string().nonempty({ message: 'Last name is required' }),
});
 
type FormData = z.infer<typeof schema>;
 
export default function CustomForm() {
  const { register, handleSubmit } = useForm<FormData>({
    resolver: zodResolver(schema),
  });
 
  // rest of component
}

For more details and usage examples, refer to the Zod documentation (opens in a new tab).

Alternative solutions

Yup

Yup (opens in a new tab) is an alternative, widely used validation library known for its expressive API and support for complex validation rules. Some key features of Yup include:

  • Declarative validation: Yup allows you to define validation rules in a declarative manner, making it easier to express complex validation logic.

  • Schema-based validation: You can create a validation schema using Yup's API and apply it to your form inputs.

  • Asynchronous validation: Yup supports asynchronous validation, allowing you to perform server-side validations or API requests.

The following code adds yup to the react-hook-form above:

import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
 
interface FormData {
  firstName: string;
  lastName: string;
}
 
const schema = yup.object().shape({
  firstName: yup.string().required('First name is required'),
  lastName: yup.string().required('Last name is required'),
});
 
export default function CustomForm() {
  const { register, handleSubmit } = useForm<FormData>({
    resolver: yupResolver(schema),
  });
 
  // rest of component
}

Comparison with Zod

FeatureZodYup
Validation ApproachTypeScript-first with runtime validationJavaScript-first with runtime validation
Schema DefinitionSchema-like objects or chaining validatorsObject schema using chaining validators
Declarative ValidationYesYes
Asynchronous ValidationYesYes
Custom Error MessagesYesYes
Conditional ValidationYesYes
TypeScript SupportExcellentGood
ExtensibilityCustom validators and typesCustom validators and types
File SizeSmaller file size due to tree-shaking supportSlightly larger file size
DocumentationComprehensive and well-documentedComprehensive and well-documented
PopularityGaining popularity in TypeScript communityLong-established and widely used
Community SupportGrowing community, active GitHub repositoryLarge community, active GitHub repository
Integration with ReactWorks well with React, compatible with form librariesWorks well with React, compatible with form libraries

For more details and usage examples, refer to the Yup documentation (opens in a new tab).


Ultimately, the choice of form handling library depends on the requirements of your project and your personal preferences. We encourage you to evaluate the options and select the one that best fits your needs. Happy coding!

Alternative solutions

React Hook Form is our top recommendations for form handling, however there are a couple of other libraries worth mentioning:

Formik

Formik (opens in a new tab) provides a rich set of features for managing form state, handling form submissions, and implementing validation logic. Some key features of Formik include:

  • Declarative approach which simplifies the code and improves readability.

  • Extensive community support which ensures continuous development, support, and regular updates.

import { Formik, Field, Form } from 'formik';
 
const initialValues = {
  firstName: '',
  lastName: '',
};
 
const onSubmit = (values: typeof initialValues) => {
  console.log(values);
};
 
export default function MyForm() {
  return (
    <Formik initialValues={initialValues} onSubmit={onSubmit}>
      <Form>
        <div>
          <label htmlFor="firstName">First Name</label>
          <Field type="text" id="firstName" name="firstName" />
        </div>
        <div>
          <label htmlFor="lastName">Last Name</label>
          <Field type="text" id="lastName" name="lastName" />
        </div>
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  );
}

For more details and usage examples, refer to the Formik documentation (opens in a new tab).

Comparison with React Hook Form

For a more in depth comparison between React Hook Form and Formik, and why we chose React Hook Form, check out this in depth comparison between React hook form and Formik (opens in a new tab).

Aspectreact-hook-formFormik
Form ValidationBuilt-in validationBuilt-in validation
Integration with ReduxCan be integrated with ReduxCan be integrated with Redux
PerformanceLightweight and performs wellSlightly heavier and may impact performance
API SimplicitySimple and intuitive APISimple and intuitive API
Community SupportActive community supportActive community support
TypeScript SupportExcellentExcellent

When to use: Choose Formik for a comprehensive form solution with built-in features like form state management, validation, and error handling.

Redux Form

Redux Form (opens in a new tab) is a popular form handling library that integrates seamlessly with Redux. It provides a powerful set of features for managing form state and validation. Redux-Form leverages the Redux ecosystem and enables you to manage form data in a central store, making it suitable for complex, data-intensive applications.

Comparison with React Hook Form

Aspectreact-hook-formRedux Form
Form ValidationBuilt-in validationBuilt-in validation
Integration with ReduxCan be integrated with ReduxDesigned specifically for Redux integration
PerformanceLightweight and performs wellCan be slightly heavier and impact performance
API SimplicitySimple and intuitive APIAPI can be complex for larger projects
Community SupportActive community supportActive community support
TypeScript SupportExcellentGood

When to use: If you have complex forms that require centralized state management using Redux. However, note that Redux Form has become less popular, and if your project doesn't heavily rely on Redux or you prefer simpler integration, React Hook Form is a recommended alternative.

Final Form

Final Form (opens in a new tab) is another robust form handling library that offers flexibility and performance. It focuses on simplicity and provides a minimal API while still supporting advanced features like field-level validation and asynchronous validation. Final Form is a good choice for developers who prefer a lightweight solution with powerful capabilities.

Comparison with React Hook Form

Aspectreact-hook-formFinal Form
Form ValidationBuilt-in validationBuilt-in validation
Integration with ReduxCan be integrated with ReduxCan be integrated with Redux
PerformanceLightweight and performs wellOptimized for performance
API SimplicitySimple and intuitive APIHighly customizable API
Community SupportActive community supportActive community support
TypeScript SupportExcellentGood

When to use: When you need a lightweight and highly customizable form library with precise control over form behavior. However, if you prefer a simpler integration with Redux, React Hook Form might be a better choice.

Additional resources


Keep up to date with any latest changes or announcements by subscribing to the newsletter below.