Effective Forms in React
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:
-
Manage form state efficiently by synchronizing data, handling user interactions, and validation errors.
-
Provide a flexible validation system to define rules, handle various scenarios, and display error messages.
-
Track and synchronize input values with state, handle changes, and manage error propagation effectively.
-
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
Feature | Zod | Yup |
---|---|---|
Validation Approach | TypeScript-first with runtime validation | JavaScript-first with runtime validation |
Schema Definition | Schema-like objects or chaining validators | Object schema using chaining validators |
Declarative Validation | Yes | Yes |
Asynchronous Validation | Yes | Yes |
Custom Error Messages | Yes | Yes |
Conditional Validation | Yes | Yes |
TypeScript Support | Excellent | Good |
Extensibility | Custom validators and types | Custom validators and types |
File Size | Smaller file size due to tree-shaking support | Slightly larger file size |
Documentation | Comprehensive and well-documented | Comprehensive and well-documented |
Popularity | Gaining popularity in TypeScript community | Long-established and widely used |
Community Support | Growing community, active GitHub repository | Large community, active GitHub repository |
Integration with React | Works well with React, compatible with form libraries | Works 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).
Aspect | react-hook-form | Formik |
---|---|---|
Form Validation | Built-in validation | Built-in validation |
Integration with Redux | Can be integrated with Redux | Can be integrated with Redux |
Performance | Lightweight and performs well | Slightly heavier and may impact performance |
API Simplicity | Simple and intuitive API | Simple and intuitive API |
Community Support | Active community support | Active community support |
TypeScript Support | Excellent | Excellent |
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
Aspect | react-hook-form | Redux Form |
---|---|---|
Form Validation | Built-in validation | Built-in validation |
Integration with Redux | Can be integrated with Redux | Designed specifically for Redux integration |
Performance | Lightweight and performs well | Can be slightly heavier and impact performance |
API Simplicity | Simple and intuitive API | API can be complex for larger projects |
Community Support | Active community support | Active community support |
TypeScript Support | Excellent | Good |
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
Aspect | react-hook-form | Final Form |
---|---|---|
Form Validation | Built-in validation | Built-in validation |
Integration with Redux | Can be integrated with Redux | Can be integrated with Redux |
Performance | Lightweight and performs well | Optimized for performance |
API Simplicity | Simple and intuitive API | Highly customizable API |
Community Support | Active community support | Active community support |
TypeScript Support | Excellent | Good |
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
- React Hook Form documentation (opens in a new tab)
- Formik documentation (opens in a new tab)
- Zod documentation (opens in a new tab)
- Yup documentation (opens in a new tab)
- Redux-Form documentation (opens in a new tab)
- Final Form documentation (opens in a new tab)
Keep up to date with any latest changes or announcements by subscribing to the newsletter below.