Form validation is essential in web applications for data integrity and enhancing user experience. It prevents incorrect data submissions and provides immediate feedback to users. In React applications, form validation is even more critical due to dynamic user interactions and state management.
React offers several ways to handle form validation which include built-in form handling, controlled components, uncontrolled components, and third-party libraries such as Formik and Yup (read our article on React Form Validation using Yup). Using the useForm
hook from the react-hook-form
library is especially effective. This hook simplifies form validation, offering a robust and flexible way to manage form state and validation. By using the useForm
hook, developers can easily implement complex validation rules, handle errors gracefully, and improve overall application performance.
Setting Up the Project
In this section, we'll outline the prerequisites needed, followed by the initial setup steps for our React project. Let's dive in!
Prerequisites
Before you begin, ensure you have:
Node.js: Download and install Node.js from its official website.
npm or yarn: These are JavaScript package managers. npm comes with Node.js, while yarn needs to be installed separately.
Basic React Knowledge: Understand React concepts like components, state, and props.
Initial Project Setup
Quickly set up a new React project, using create-react-app
. Open your terminal and run:
npx create-react-app react-form-validation
Next, navigate to the project directory:
cd react-form-validation
To handle form validation, we'll use the react-hook-form
library. Install it by running the following command in your project directory:
npm install react-hook-form
In your App.js
file, clear out the existing code and import the useForm
dependency:
import { useForm } from 'react-hook-form';
Basic Usage of the useForm Hook
In this section, we will explain a straightforward syntax for using the useForm
hook.
Defining Form Fields with useForm
To set up a form, define the input fields needed within your form component. For instance, if you want fields for name, email, and password, you can create them like this:
function MyForm() {
const { register, handleSubmit } = useForm();
return (
<form>
<input placeholder="Name" />
<input placeholder="Email" type="email" />
<input placeholder="Password" type="password" />
<button type="submit">Submit</button>
</form>
);
}
Afterward, register these input fields with useForm
using the register
method. This step ensures that react-hook-form
can manage their state and validation:
const { register, handleSubmit } = useForm();
{/* Inside the form JSX */}
<input {...register('name')} placeholder="Name" />
<input {...register('email')} placeholder="Email" type="email" />
<input {...register('password')} placeholder="Password" type="password" />
Each input field utilizes the register
function to associate itself with a unique identifier, such as 'name'
, 'email'
, or 'password'
. This enables react-hook-form
to handle their values and validation.
To handle form submission, utilize the handleSubmit
function from useForm
and define a callback function to process the form data:
const onSubmit = (data) => {
console.log(data); // Handle form data
};
{/* Inside the form JSX */}
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} placeholder="Name" />
<input {...register('email')} placeholder="Email" type="email" />
<input {...register('password')} placeholder="Password" type="password" />
<button type="submit">Submit</button>
</form>
The onSubmit
function receives the form data as an argument, allowing you to process it or send it to a server as needed.
Adding Validation Rules
react-hook-form
provides various validation options to ensure form fields meet certain criteria. These options include:
required
: Checks if a field is filled.minLength
andmaxLength
: Ensures the input meets length requirements.pattern
: Validates the input against a regular expression.validate
: Allows custom validation logic.
To set validation rules, pass an object with validation criteria to the register function for each field.
import React from 'react';
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input
{...register('name', { required: true, minLength: 2 })}
placeholder="Name"
/>
{errors.name && <span>Name is required and must be at least 2 characters long</span>}
</div>
<div>
<input
{...register('email', { required: true, pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/ })}
placeholder="Email"
type="email"
/>
{errors.email && <span>Please enter a valid email</span>}
</div>
<div>
<input
{...register('password', { required: true, minLength: 6 })}
placeholder="Password"
type="password"
/>
{errors.password && <span>Password is required and must be at least 6 characters long</span>}
</div>
<button type="submit">Submit</button>
</form>
);
}
export default MyForm;
In this example:
The
name
field is required and must be at least 2 characters long.The
email
field is required and must match a valid email pattern.The
password
field is required and must be at least 6 characters long.
Custom Validation Logic
For custom validation, use the validate
option, which accepts a function returning true
or an error
message.
Consider the following code snippet:
import React from 'react';
import { useForm } from 'react-hook-form';
function MyForm() {
// Destructuring the necessary functions and objects from the useForm hook
const { register, handleSubmit, formState: { errors } } = useForm();
// Function to handle form submission
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* Input field for username */}
<div>
<input
// Registering the username field with custom validation rules
{...register('username', {
// Required validation
required: 'Username is required',
// Custom validation to ensure username includes "admin"
validate: (value) => value.includes('admin') || 'Username must include "admin"'
})}
placeholder="Username"
/>
{/* Displaying error message if username validation fails */}
{errors.username && <span>{errors.username.message}</span>}
</div>
{/* Submit button */}
<button type="submit">Submit</button>
</form>
);
}
export default MyForm;
This code sets up a form with a username input field that must be filled out and includes custom validation logic to ensure that the username contains the substring "admin". If the validation fails, an error message is displayed.
Dynamically Displaying Validation Errors
To enable error messages to display when users interact with input fields, such as clicking or moving away from them, you can employ the useForm
hook along with the watch
function and the onBlur
event.
Here's how to achieve this:
import React from 'react';
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit, formState: { errors }, watch, trigger } = useForm();
const onSubmit = (data) => {
console.log(data);
};
// Watch form fields to trigger validation on change
const watchFields = watch();
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input
{...register('name', {
required: 'Name is required',
minLength: { value: 2, message: 'Name must be at least 2 characters long' }
})}
placeholder="Name"
onBlur={() => trigger('name')}
/>
{errors.name && <span className="error-message">{errors.name.message}</span>}
</div>
<div>
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/,
message: 'Please enter a valid email'
}
})}
placeholder="Email"
type="email"
onBlur={() => trigger('email')}
/>
{errors.email && <span className="error-message">{errors.email.message}</span>}
</div>
<div>
<input
{...register('password', {
required: 'Password is required',
minLength: {
value: 6,
message: 'Password must be at least 6 characters long'
}
})}
placeholder="Password"
type="password"
onBlur={() => trigger('password')}
/>
{errors.password && <span className="error-message">{errors.password.message}</span>}
</div>
<div>
<input
{...register('username', {
required: 'Username is required',
validate: (value) => value.includes('admin') || 'Username must include "admin"'
})}
placeholder="Username"
onBlur={() => trigger('username')}
/>
{errors.username && <span className="error-message">{errors.username.message}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
}
export default MyForm;
Here, we utilized the watch
function to track changes in the form fields and attached the onBlur
event to each input field. When the user moves away from the field, call trigger('fieldName')
to validate that specific field.
Below is a preview of the image dynamically displaying the errors if the input does not meet the criteria specified in the form validation rules we created.
After successfully validating the input, we'll be able to view it in the console.
Conclusion
Form validation is essential in web applications to ensure data integrity and provide immediate feedback to users. The useForm
hook from the react-hook-form
library offers a streamlined approach for managing form state and validation in React applications. By leveraging this hook, developers can easily define form fields, apply complex validation rules, and handle errors gracefully. This not only simplifies the implementation process but also enhances the user experience by providing real-time validation feedback. Integrating react-hook-form results in more efficient and user-friendly forms, making it a valuable tool for React developers.